tinker-pay-ruby-sdk 0.1.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 (39) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec_status +31 -0
  3. data/.rubocop.yml +64 -0
  4. data/Gemfile +11 -0
  5. data/README.md +166 -0
  6. data/Rakefile +8 -0
  7. data/lib/tinker/api/base_manager.rb +47 -0
  8. data/lib/tinker/api/transaction_manager.rb +21 -0
  9. data/lib/tinker/auth/authentication_manager.rb +66 -0
  10. data/lib/tinker/config/configuration.rb +19 -0
  11. data/lib/tinker/config/endpoints.rb +13 -0
  12. data/lib/tinker/enum/gateway.rb +11 -0
  13. data/lib/tinker/enum/payment_status.rb +12 -0
  14. data/lib/tinker/exception/api_exception.rb +14 -0
  15. data/lib/tinker/exception/authentication_exception.rb +15 -0
  16. data/lib/tinker/exception/client_exception.rb +15 -0
  17. data/lib/tinker/exception/exception_code.rb +15 -0
  18. data/lib/tinker/exception/invalid_payload_exception.rb +15 -0
  19. data/lib/tinker/exception/network_exception.rb +15 -0
  20. data/lib/tinker/exception/webhook_exception.rb +15 -0
  21. data/lib/tinker/http/http_client.rb +48 -0
  22. data/lib/tinker/model/dto/callback_data_dto.rb +40 -0
  23. data/lib/tinker/model/dto/initiate_payment_request_dto.rb +44 -0
  24. data/lib/tinker/model/dto/initiation_data_dto.rb +30 -0
  25. data/lib/tinker/model/dto/query_data_dto.rb +40 -0
  26. data/lib/tinker/model/dto/query_payment_request_dto.rb +25 -0
  27. data/lib/tinker/model/transaction.rb +49 -0
  28. data/lib/tinker/payments.rb +34 -0
  29. data/lib/tinker/version.rb +5 -0
  30. data/lib/tinker/webhook/dto/invoice_event_data_dto.rb +35 -0
  31. data/lib/tinker/webhook/dto/payment_event_data_dto.rb +40 -0
  32. data/lib/tinker/webhook/dto/settlement_event_data_dto.rb +33 -0
  33. data/lib/tinker/webhook/dto/subscription_event_data_dto.rb +35 -0
  34. data/lib/tinker/webhook/webhook_event.rb +75 -0
  35. data/lib/tinker/webhook/webhook_handler.rb +35 -0
  36. data/lib/tinker/webhook/webhook_meta.rb +15 -0
  37. data/lib/tinker/webhook/webhook_security.rb +14 -0
  38. data/lib/tinker.rb +33 -0
  39. metadata +110 -0
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tinker
4
+ module Model
5
+ module Dto
6
+ class CallbackDataDto
7
+ attr_reader :id, :status, :reference, :amount, :currency, :paid_at, :created_at, :channel
8
+
9
+ def initialize(data)
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
17
+ @reference = data['reference']
18
+ @amount = data['amount'].to_f
19
+ @currency = data['currency']
20
+ @paid_at = data['paid_at']
21
+ @created_at = data['created_at']
22
+ @channel = data['channel']
23
+ end
24
+
25
+ def to_hash
26
+ {
27
+ id: @id,
28
+ status: @status,
29
+ reference: @reference,
30
+ amount: @amount,
31
+ currency: @currency,
32
+ paid_at: @paid_at,
33
+ created_at: @created_at,
34
+ channel: @channel
35
+ }
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tinker
4
+ module Model
5
+ module Dto
6
+ class InitiatePaymentRequestDto
7
+ attr_reader :amount, :currency, :gateway, :merchant_reference, :return_url,
8
+ :customer_phone, :customer_email, :transaction_desc, :metadata
9
+
10
+ def initialize(amount:, currency:, gateway:, merchant_reference:, return_url:,
11
+ customer_phone: nil, customer_email: nil, transaction_desc: nil, metadata: nil)
12
+ @amount = amount
13
+ @currency = currency
14
+ @gateway = gateway
15
+ @merchant_reference = merchant_reference
16
+ @return_url = return_url
17
+ @customer_phone = customer_phone
18
+ @customer_email = customer_email
19
+ @transaction_desc = transaction_desc
20
+ @metadata = metadata
21
+ end
22
+
23
+ def to_hash
24
+ gateway_value = @gateway.is_a?(String) ? @gateway : @gateway.to_s
25
+
26
+ payload = {
27
+ amount: @amount,
28
+ currency: @currency,
29
+ gateway: gateway_value,
30
+ merchantReference: @merchant_reference,
31
+ returnUrl: @return_url
32
+ }
33
+
34
+ payload[:customerPhone] = @customer_phone if @customer_phone
35
+ payload[:customerEmail] = @customer_email if @customer_email
36
+ payload[:transactionDesc] = @transaction_desc if @transaction_desc
37
+ payload[:metadata] = @metadata if @metadata
38
+
39
+ payload
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tinker
4
+ module Model
5
+ module Dto
6
+ class InitiationDataDto
7
+ attr_reader :payment_reference, :status, :authorization_url
8
+
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']
18
+ end
19
+
20
+ def to_hash
21
+ {
22
+ payment_reference: @payment_reference,
23
+ status: @status,
24
+ authorization_url: @authorization_url
25
+ }
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tinker
4
+ module Model
5
+ module Dto
6
+ class QueryDataDto
7
+ attr_reader :id, :status, :reference, :amount, :currency, :paid_at, :created_at, :channel
8
+
9
+ def initialize(data)
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
17
+ @reference = data['reference']
18
+ @amount = data['amount'].to_f
19
+ @currency = data['currency']
20
+ @paid_at = data['paid_at']
21
+ @created_at = data['created_at']
22
+ @channel = data['channel']
23
+ end
24
+
25
+ def to_hash
26
+ {
27
+ id: @id,
28
+ status: @status,
29
+ reference: @reference,
30
+ amount: @amount,
31
+ currency: @currency,
32
+ paid_at: @paid_at,
33
+ created_at: @created_at,
34
+ channel: @channel
35
+ }
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tinker
4
+ module Model
5
+ module Dto
6
+ class QueryPaymentRequestDto
7
+ attr_reader :payment_reference, :gateway
8
+
9
+ def initialize(payment_reference:, gateway:)
10
+ @payment_reference = payment_reference
11
+ @gateway = gateway
12
+ end
13
+
14
+ def to_hash
15
+ gateway_value = @gateway.is_a?(String) ? @gateway : @gateway.to_s
16
+
17
+ {
18
+ payment_reference: @payment_reference,
19
+ gateway: gateway_value
20
+ }
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tinker
4
+ module Model
5
+ class Transaction
6
+ attr_reader :status, :initiation_data, :query_data, :callback_data
7
+
8
+ def initialize(data)
9
+ if data['payment_reference'] && !data['id']
10
+ @initiation_data = Dto::InitiationDataDto.new(data)
11
+ @query_data = nil
12
+ @callback_data = nil
13
+ @status = @initiation_data.status
14
+ elsif data['id'] && data['reference']
15
+ @initiation_data = nil
16
+ @query_data = Dto::QueryDataDto.new(data)
17
+ @callback_data = Dto::CallbackDataDto.new(data)
18
+ @status = @query_data.status
19
+ else
20
+ @initiation_data = nil
21
+ @query_data = nil
22
+ @callback_data = nil
23
+ status_value = data['status'] || 'pending'
24
+ @status = begin
25
+ Enum::PaymentStatus.const_get(status_value.upcase)
26
+ rescue NameError
27
+ Enum::PaymentStatus::PENDING
28
+ end
29
+ end
30
+ end
31
+
32
+ def successful?
33
+ @status == Enum::PaymentStatus::SUCCESS
34
+ end
35
+
36
+ def pending?
37
+ @status == Enum::PaymentStatus::PENDING
38
+ end
39
+
40
+ def cancelled?
41
+ @status == Enum::PaymentStatus::CANCELLED
42
+ end
43
+
44
+ def failed?
45
+ @status == Enum::PaymentStatus::FAILED
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'version'
4
+ require_relative 'config/configuration'
5
+ require_relative 'config/endpoints'
6
+ require_relative 'http/http_client'
7
+ require_relative 'auth/authentication_manager'
8
+ require_relative 'api/base_manager'
9
+ require_relative 'api/transaction_manager'
10
+ require_relative 'webhook/webhook_handler'
11
+
12
+ module Tinker
13
+ class Payments
14
+ def initialize(api_public_key:, api_secret_key:, http_client: nil)
15
+ @config = Config::Configuration.new(api_public_key: api_public_key, api_secret_key: api_secret_key)
16
+ @http_client = http_client || Http::HttpClient.new
17
+ @auth_manager = Auth::AuthenticationManager.new(config: @config, http_client: @http_client)
18
+ @transactions_manager = nil
19
+ @webhook_handler = nil
20
+ end
21
+
22
+ def transactions
23
+ @transactions ||= Api::TransactionManager.new(
24
+ config: @config,
25
+ http_client: @http_client,
26
+ auth_manager: @auth_manager
27
+ )
28
+ end
29
+
30
+ def webhooks
31
+ @webhooks ||= Webhook::WebhookHandler.new
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tinker
4
+ VERSION = '0.1.0'
5
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tinker
4
+ module Webhook
5
+ module Dto
6
+ class InvoiceEventDataDto
7
+ attr_reader :id, :status, :invoice_number, :amount, :currency, :subscription_id, :created_at, :paid_at
8
+
9
+ def initialize(data)
10
+ @id = data['id']
11
+ @status = data['status']
12
+ @invoice_number = data['invoice_number'] || ''
13
+ @amount = data['amount'].to_f
14
+ @currency = data['currency']
15
+ @subscription_id = data['subscription_id'] || ''
16
+ @created_at = data['created_at'] || ''
17
+ @paid_at = data['paid_at']
18
+ end
19
+
20
+ def to_hash
21
+ {
22
+ id: @id,
23
+ status: @status,
24
+ invoice_number: @invoice_number,
25
+ amount: @amount,
26
+ currency: @currency,
27
+ subscription_id: @subscription_id,
28
+ created_at: @created_at,
29
+ paid_at: @paid_at
30
+ }
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tinker
4
+ module Webhook
5
+ module Dto
6
+ class PaymentEventDataDto
7
+ attr_reader :id, :status, :reference, :amount, :currency, :channel, :created_at, :paid_at
8
+
9
+ def initialize(data)
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
17
+ @reference = data['reference']
18
+ @amount = data['amount'].to_f
19
+ @currency = data['currency']
20
+ @channel = data['channel']
21
+ @created_at = data['created_at']
22
+ @paid_at = data['paid_at']
23
+ end
24
+
25
+ def to_hash
26
+ {
27
+ 'id' => @id,
28
+ 'status' => @status.to_s,
29
+ 'reference' => @reference,
30
+ 'amount' => @amount,
31
+ 'currency' => @currency,
32
+ 'channel' => @channel,
33
+ 'created_at' => @created_at,
34
+ 'paid_at' => @paid_at
35
+ }
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tinker
4
+ module Webhook
5
+ module Dto
6
+ class SettlementEventDataDto
7
+ attr_reader :id, :status, :amount, :currency, :settlement_date, :created_at, :processed_at
8
+
9
+ def initialize(data)
10
+ @id = data['id']
11
+ @status = data['status']
12
+ @amount = data['amount'].to_f
13
+ @currency = data['currency']
14
+ @settlement_date = data['settlement_date'] || ''
15
+ @created_at = data['created_at'] || ''
16
+ @processed_at = data['processed_at']
17
+ end
18
+
19
+ def to_hash
20
+ {
21
+ id: @id,
22
+ status: @status,
23
+ amount: @amount,
24
+ currency: @currency,
25
+ settlement_date: @settlement_date,
26
+ created_at: @created_at,
27
+ processed_at: @processed_at
28
+ }
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tinker
4
+ module Webhook
5
+ module Dto
6
+ class SubscriptionEventDataDto
7
+ attr_reader :id, :status, :plan_id, :customer_id, :created_at, :cancelled_at, :paused_at, :reactivated_at
8
+
9
+ def initialize(data)
10
+ @id = data['id']
11
+ @status = data['status']
12
+ @plan_id = data['plan_id'] || ''
13
+ @customer_id = data['customer_id'] || ''
14
+ @created_at = data['created_at'] || ''
15
+ @cancelled_at = data['cancelled_at']
16
+ @paused_at = data['paused_at']
17
+ @reactivated_at = data['reactivated_at']
18
+ end
19
+
20
+ def to_hash
21
+ {
22
+ id: @id,
23
+ status: @status,
24
+ plan_id: @plan_id,
25
+ customer_id: @customer_id,
26
+ created_at: @created_at,
27
+ cancelled_at: @cancelled_at,
28
+ paused_at: @paused_at,
29
+ reactivated_at: @reactivated_at
30
+ }
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tinker
4
+ module Webhook
5
+ class WebhookEvent
6
+ attr_reader :id, :type, :source, :timestamp, :data, :meta, :security
7
+
8
+ def initialize(payload)
9
+ @id = payload['id']
10
+ @type = payload['type']
11
+ @source = payload['source']
12
+ @timestamp = payload['timestamp']
13
+ @data = create_event_data(payload['data'], @source)
14
+ @meta = WebhookMeta.new(payload['meta'] || {})
15
+ @security = WebhookSecurity.new(payload['security'] || {})
16
+ end
17
+
18
+ def payment_event?
19
+ @source == 'payment'
20
+ end
21
+
22
+ def subscription_event?
23
+ @source == 'subscription'
24
+ end
25
+
26
+ def invoice_event?
27
+ @source == 'invoice'
28
+ end
29
+
30
+ def settlement_event?
31
+ @source == 'settlement'
32
+ end
33
+
34
+ def payment_data
35
+ @data.is_a?(Dto::PaymentEventDataDto) ? @data : nil
36
+ end
37
+
38
+ def subscription_data
39
+ @data.is_a?(Dto::SubscriptionEventDataDto) ? @data : nil
40
+ end
41
+
42
+ def invoice_data
43
+ @data.is_a?(Dto::InvoiceEventDataDto) ? @data : nil
44
+ end
45
+
46
+ def settlement_data
47
+ @data.is_a?(Dto::SettlementEventDataDto) ? @data : nil
48
+ end
49
+
50
+ def to_transaction
51
+ return nil unless payment_event?
52
+ return nil unless @data.is_a?(Dto::PaymentEventDataDto)
53
+
54
+ Model::Transaction.new(@data.to_hash)
55
+ end
56
+
57
+ private
58
+
59
+ def create_event_data(data, source)
60
+ case source
61
+ when 'payment'
62
+ Dto::PaymentEventDataDto.new(data)
63
+ when 'subscription'
64
+ Dto::SubscriptionEventDataDto.new(data)
65
+ when 'invoice'
66
+ Dto::InvoiceEventDataDto.new(data)
67
+ when 'settlement'
68
+ Dto::SettlementEventDataDto.new(data)
69
+ else
70
+ raise Exception::InvalidPayloadException.new("Unknown webhook source: #{source}", Exception::ExceptionCode::INVALID_PAYLOAD)
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module Tinker
6
+ module Webhook
7
+ class WebhookHandler
8
+ def handle(payload)
9
+ data = if payload.is_a?(String)
10
+ JSON.parse(payload)
11
+ else
12
+ payload
13
+ end
14
+
15
+ raise Exception::InvalidPayloadException, 'Webhook payload must be a hash' unless data.is_a?(Hash)
16
+
17
+ WebhookEvent.new(data)
18
+ rescue JSON::ParserError => e
19
+ raise Exception::InvalidPayloadException, "Invalid JSON payload: #{e.message}"
20
+ end
21
+
22
+ def handle_from_request(request_body = nil)
23
+ payload = request_body || $stdin.read
24
+ raise Exception::WebhookException, 'Unable to read request body' if payload.nil? || payload.empty?
25
+
26
+ handle(payload)
27
+ end
28
+
29
+ def handle_as_transaction(payload)
30
+ event = handle(payload)
31
+ event.to_transaction
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tinker
4
+ module Webhook
5
+ class WebhookMeta
6
+ attr_reader :version, :app_id, :gateway
7
+
8
+ def initialize(meta = {})
9
+ @version = meta['version'] || '1.0'
10
+ @app_id = meta['app_id'] || ''
11
+ @gateway = meta['gateway']
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tinker
4
+ module Webhook
5
+ class WebhookSecurity
6
+ attr_reader :signature, :algorithm
7
+
8
+ def initialize(security = {})
9
+ @signature = security['signature'] || ''
10
+ @algorithm = security['algorithm'] || 'HMAC-SHA256'
11
+ end
12
+ end
13
+ end
14
+ end
data/lib/tinker.rb ADDED
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'tinker/version'
4
+ require_relative 'tinker/config/configuration'
5
+ require_relative 'tinker/config/endpoints'
6
+ require_relative 'tinker/exception/exception_code'
7
+ require_relative 'tinker/exception/api_exception'
8
+ require_relative 'tinker/exception/network_exception'
9
+ require_relative 'tinker/exception/authentication_exception'
10
+ require_relative 'tinker/exception/invalid_payload_exception'
11
+ require_relative 'tinker/exception/webhook_exception'
12
+ require_relative 'tinker/exception/client_exception'
13
+ require_relative 'tinker/enum/gateway'
14
+ require_relative 'tinker/enum/payment_status'
15
+ require_relative 'tinker/http/http_client'
16
+ require_relative 'tinker/auth/authentication_manager'
17
+ require_relative 'tinker/api/base_manager'
18
+ require_relative 'tinker/api/transaction_manager'
19
+ require_relative 'tinker/model/dto/initiate_payment_request_dto'
20
+ require_relative 'tinker/model/dto/query_payment_request_dto'
21
+ require_relative 'tinker/model/dto/initiation_data_dto'
22
+ require_relative 'tinker/model/dto/query_data_dto'
23
+ require_relative 'tinker/model/dto/callback_data_dto'
24
+ require_relative 'tinker/model/transaction'
25
+ require_relative 'tinker/webhook/dto/payment_event_data_dto'
26
+ require_relative 'tinker/webhook/dto/subscription_event_data_dto'
27
+ require_relative 'tinker/webhook/dto/invoice_event_data_dto'
28
+ require_relative 'tinker/webhook/dto/settlement_event_data_dto'
29
+ require_relative 'tinker/webhook/webhook_meta'
30
+ require_relative 'tinker/webhook/webhook_security'
31
+ require_relative 'tinker/webhook/webhook_event'
32
+ require_relative 'tinker/webhook/webhook_handler'
33
+ require_relative 'tinker/payments'