transbank-sdk 1.0.1

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 (51) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.travis.yml +8 -0
  4. data/CHANGELOG.md +13 -0
  5. data/Dockerfile +6 -0
  6. data/Gemfile +6 -0
  7. data/LICENSE.md +11 -0
  8. data/Makefile +24 -0
  9. data/README.md +87 -0
  10. data/Rakefile +10 -0
  11. data/bin/console +14 -0
  12. data/bin/setup +8 -0
  13. data/docker-compose.yml +20 -0
  14. data/lib/transbank/sdk.rb +23 -0
  15. data/lib/transbank/sdk/onepay/base.rb +115 -0
  16. data/lib/transbank/sdk/onepay/errors/errors.rb +17 -0
  17. data/lib/transbank/sdk/onepay/errors/integration_type_error.rb +8 -0
  18. data/lib/transbank/sdk/onepay/errors/invalid_options_error.rb +8 -0
  19. data/lib/transbank/sdk/onepay/errors/item_error.rb +8 -0
  20. data/lib/transbank/sdk/onepay/errors/refund_create_error.rb +8 -0
  21. data/lib/transbank/sdk/onepay/errors/response_error.rb +8 -0
  22. data/lib/transbank/sdk/onepay/errors/shopping_cart_error.rb +8 -0
  23. data/lib/transbank/sdk/onepay/errors/signature_error.rb +8 -0
  24. data/lib/transbank/sdk/onepay/errors/transaction_commit_error.rb +8 -0
  25. data/lib/transbank/sdk/onepay/errors/transaction_create_error.rb +8 -0
  26. data/lib/transbank/sdk/onepay/errors/transbank_error.rb +9 -0
  27. data/lib/transbank/sdk/onepay/models/channels.rb +15 -0
  28. data/lib/transbank/sdk/onepay/models/item.rb +103 -0
  29. data/lib/transbank/sdk/onepay/models/models.rb +10 -0
  30. data/lib/transbank/sdk/onepay/models/refund.rb +51 -0
  31. data/lib/transbank/sdk/onepay/models/shopping_cart.rb +65 -0
  32. data/lib/transbank/sdk/onepay/models/transaction.rb +141 -0
  33. data/lib/transbank/sdk/onepay/requests/refund_create_request.rb +45 -0
  34. data/lib/transbank/sdk/onepay/requests/request.rb +18 -0
  35. data/lib/transbank/sdk/onepay/requests/requests.rb +9 -0
  36. data/lib/transbank/sdk/onepay/requests/transaction_commit_request.rb +48 -0
  37. data/lib/transbank/sdk/onepay/requests/transaction_create_request.rb +80 -0
  38. data/lib/transbank/sdk/onepay/responses/refund_create_response.rb +24 -0
  39. data/lib/transbank/sdk/onepay/responses/response.rb +18 -0
  40. data/lib/transbank/sdk/onepay/responses/responses.rb +9 -0
  41. data/lib/transbank/sdk/onepay/responses/transaction_commit_response.rb +39 -0
  42. data/lib/transbank/sdk/onepay/responses/transaction_create_response.rb +32 -0
  43. data/lib/transbank/sdk/onepay/utils/json_utils.rb +73 -0
  44. data/lib/transbank/sdk/onepay/utils/net_helper.rb +38 -0
  45. data/lib/transbank/sdk/onepay/utils/request_builder.rb +88 -0
  46. data/lib/transbank/sdk/onepay/utils/signature_utils.rb +49 -0
  47. data/lib/transbank/sdk/onepay/utils/utils.rb +9 -0
  48. data/lib/transbank/sdk/version.rb +5 -0
  49. data/sdk_test.sh +2 -0
  50. data/transbank-sdk.gemspec +33 -0
  51. metadata +220 -0
@@ -0,0 +1,45 @@
1
+ module Transbank
2
+ module Onepay
3
+ # Creates a Refund request
4
+ class RefundCreateRequest
5
+ include Request
6
+ attr_accessor :nullify_amount, :occ, :external_unique_number,
7
+ :authorization_code, :issued_at, :signature
8
+
9
+ # These are the params used to build this class's @signature
10
+ SIGNATURE_PARAMS = [:occ,
11
+ :external_unique_number,
12
+ :authorization_code,
13
+ :issued_at,
14
+ :nullify_amount].freeze
15
+
16
+ # @param nullify_amount [Integer, nil] The total amount of the [Transaction] to Refund.
17
+ # No partial refunds are possible
18
+ # @param external_unique_number [String] a unique value (per Merchant, not global) that is used to identify a Transaction
19
+ # @param occ [String] Merchant purchase order
20
+ # @param authorization_code [String] a string returned when [Transaction]#commit completes correctly
21
+ # @param issued_at [Integer, nil] a timestamp
22
+ # @param signature [String, nil] a hashed string to verify the data
23
+ def initialize(nullify_amount: nil,
24
+ occ: nil,
25
+ external_unique_number: nil,
26
+ authorization_code: nil,
27
+ issued_at: nil,
28
+ signature: nil)
29
+ @nullify_amount = nullify_amount
30
+ @occ = occ
31
+ @external_unique_number = external_unique_number
32
+ @authorization_code = authorization_code
33
+ @issued_at = issued_at
34
+ @signature = signature
35
+ end
36
+
37
+ # Create and set the signature for this instance of RefundCreateRequest
38
+ # @return [RefundCreateRequest] returns self
39
+ def sign(secret)
40
+ @signature = signature_for(to_data, secret)
41
+ self
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,18 @@
1
+ module Transbank
2
+ module Onepay
3
+ # Base module with methods & attributes common to Requests
4
+ module Request
5
+ include Utils::JSONUtils, Utils::SignatureUtils
6
+ attr_accessor :api_key
7
+ attr_accessor :app_key
8
+
9
+ # Set the request's @api_key overriding the one in
10
+ # the [Base] class
11
+ def set_keys_from_options(options)
12
+ transform_hash_keys(options)
13
+ new_api_key = options.fetch(:api_key, nil)
14
+ self.api_key = new_api_key unless new_api_key.nil?
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,9 @@
1
+ require 'transbank/sdk/onepay/requests/request'
2
+ require 'transbank/sdk/onepay/requests/transaction_create_request'
3
+ require 'transbank/sdk/onepay/requests/transaction_commit_request'
4
+ require 'transbank/sdk/onepay/requests/refund_create_request'
5
+
6
+ module Transbank
7
+ module Onepay
8
+ end
9
+ end
@@ -0,0 +1,48 @@
1
+ module Transbank
2
+ module Onepay
3
+ # Creates a request to Transbank attempting to commit a [Transaction]
4
+ class TransactionCommitRequest
5
+ include Request
6
+ attr_reader :occ, :external_unique_number, :issued_at, :signature
7
+
8
+ SIGNATURE_PARAMS = [:occ,
9
+ :external_unique_number,
10
+ :issued_at].freeze
11
+ # @param occ [String] Merchant purchase order
12
+ # @param external_unique_number [String] a unique value (per Merchant, not global) that is used to identify a Transaction
13
+ # @param issued_at [Integer] timestamp for when the transaction commit request was created
14
+ def initialize(occ, external_unique_number, issued_at)
15
+ self.occ = occ
16
+ self.external_unique_number = external_unique_number
17
+ self.issued_at = issued_at
18
+ @signature = nil
19
+ end
20
+
21
+ # @param occ [String] Merchant purchase order
22
+ def occ=(occ)
23
+ raise Errors::TransactionCommitError, 'occ cannot be null.' if occ.nil?
24
+ @occ = occ
25
+ end
26
+
27
+ # @param external_unique_number [String] a unique value (per Merchant, not global) that is used to identify a Transaction
28
+ def external_unique_number=(external_unique_number)
29
+ raise Errors::TransactionCommitError, 'external_unique_number cannot be null.' if external_unique_number.nil?
30
+ @external_unique_number = external_unique_number
31
+ end
32
+
33
+ # @param issued_at [Integer] timestamp for when the transaction commit request was created
34
+ def issued_at=(issued_at)
35
+ raise Errors::TransactionCommitError, 'issued_at cannot be null.' if issued_at.nil?
36
+ @issued_at = issued_at
37
+ end
38
+
39
+ # Create a signature string and assign it to @signature
40
+ # @return [TransactionCommitRequest] self
41
+ def sign(secret)
42
+ @signature = signature_for(to_data, secret)
43
+ self
44
+ end
45
+
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,80 @@
1
+ module Transbank
2
+ module Onepay
3
+ # Create a payload to create [Transaction] on Transbank
4
+ class TransactionCreateRequest
5
+ include Request
6
+
7
+ attr_accessor :external_unique_number, :total, :items_quantity, :issued_at,
8
+ :items, :callback_url, :channel, :app_scheme, :signature
9
+ attr_reader :generate_ott_qr_code
10
+
11
+ SIGNATURE_PARAMS = [:external_unique_number,
12
+ :total,
13
+ :items_quantity,
14
+ :issued_at,
15
+ :callback_url].freeze
16
+ # @param opts [Hash] options hash with params needed to initialize this
17
+ # @param external_unique_number [String] Unique identifier (per Merchant) of the [Transaction] that
18
+ # @param total [Numeric] the total amount to pay for the items
19
+ # @param items_quantity [Numeric] the quantity of items on the shopping cart
20
+ # @param items [Array<Item] the items on the shopping cart
21
+ # @param issued_at [Numeric] timestamp at the moment the transaction is created
22
+ # @param callback_url [String] used when the channel is mobile, to be able to finish the [Transaction]
23
+ # @param channel [String] The channel the operation is made on. Valid values
24
+ # are on the [Channel] class
25
+ # @param app_scheme [String] identificator for the Merchant's app
26
+ # @param signature [String, nil] a hashstring created for verification purposes
27
+ def initialize(opts = {})
28
+ self.external_unique_number = opts.fetch(:external_unique_number)
29
+ self.total = opts.fetch(:total)
30
+ self.items_quantity = opts.fetch(:items_quantity)
31
+ self.items = opts.fetch(:items)
32
+ self.issued_at = opts.fetch(:issued_at)
33
+ self.callback_url = opts.fetch(:callback_url)
34
+ channel = opts.fetch(:channel, Channel::WEB)
35
+ self.channel = channel
36
+ self.app_scheme = opts.fetch(:app_scheme, '')
37
+ self.signature = nil
38
+ # This is never anything but true, but it is required by the server
39
+ @generate_ott_qr_code = true
40
+ end
41
+
42
+ def external_unique_number=(external_unique_number)
43
+ raise Errors::TransactionCreateError, 'External Unique Number cannot be null.' if external_unique_number.nil?
44
+ @external_unique_number = external_unique_number
45
+ end
46
+
47
+ def total=(total)
48
+ raise Errors::TransactionCreateError, 'Total cannot be null.' if total.nil?
49
+ raise Errors::TransactionCreateError, 'Total cannot be less than zero.' if total < 0
50
+ @total = total
51
+ end
52
+
53
+ def items_quantity=(items_quantity)
54
+ raise Errors::TransactionCreateError, 'Items quantity cannot be null.' if items_quantity.nil?
55
+ raise Errors::TransactionCreateError, 'Items quantity cannot be less than zero.' if items_quantity < 0
56
+ @items_quantity = items_quantity
57
+ end
58
+
59
+ def items=(items)
60
+ raise Errors::TransactionCreateError, 'Items must not be empty.' if items.empty?
61
+ @items = items
62
+ end
63
+
64
+ def callback_url=(callback_url)
65
+ raise Errors::TransactionCreateError, 'Callback url cannot be null.' if callback_url.nil?
66
+ @callback_url = callback_url
67
+ end
68
+
69
+ def channel=(channel)
70
+ raise Errors::TransactionCreateError, 'Channel cannot be null.' if channel.nil?
71
+ channel
72
+ end
73
+
74
+ def sign(secret)
75
+ @signature = signature_for(to_data, secret)
76
+ self
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,24 @@
1
+ module Transbank
2
+ module Onepay
3
+ # Serializes the response to a RefundCreateRequest
4
+ class RefundCreateResponse
5
+ include Response
6
+ attr_accessor :occ, :external_unique_number, :reverse_code, :issued_at,
7
+ :signature
8
+ # @raise []RefundCreateError] if the responseCode from the service is not 'OK'
9
+ def initialize(json)
10
+ unless json.fetch('responseCode').downcase == 'ok'
11
+ raise Errors::RefundCreateError, "#{json.fetch('responseCode')} : #{json.fetch('description')}"
12
+ end
13
+ result = json.fetch('result')
14
+ @response_code = json.fetch('responseCode')
15
+ @description = json.fetch('description')
16
+ @occ = result.fetch('occ')
17
+ @external_unique_number = result.fetch('externalUniqueNumber')
18
+ @reverse_code = result.fetch('reverseCode')
19
+ @issued_at = result.fetch('issuedAt')
20
+ @signature = result.fetch('signature')
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,18 @@
1
+ module Transbank
2
+ module Onepay
3
+ # Shared methods and attributes between all types of Responses
4
+ module Response
5
+ include Utils::SignatureUtils, Utils::JSONUtils
6
+ attr_accessor :response_code
7
+ attr_accessor :description
8
+
9
+ def response_ok?
10
+ response_code.downcase == 'ok'
11
+ end
12
+
13
+ def full_description
14
+ "#{ response_code } : #{ description }"
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,9 @@
1
+ require 'transbank/sdk/onepay/responses/response'
2
+ require 'transbank/sdk/onepay/responses/refund_create_response'
3
+ require 'transbank/sdk/onepay/responses/transaction_commit_response'
4
+ require 'transbank/sdk/onepay/responses/transaction_create_response'
5
+
6
+ module Transbank
7
+ module Onepay
8
+ end
9
+ end
@@ -0,0 +1,39 @@
1
+ module Transbank
2
+ module Onepay
3
+ # Serializes the response to a TransactionCommitRequest
4
+ class TransactionCommitResponse
5
+ include Response
6
+
7
+ attr_accessor :occ, :authorization_code, :signature, :transaction_desc,
8
+ :buy_order, :issued_at, :amount, :installments_amount,
9
+ :installments_number
10
+
11
+ SIGNATURE_PARAMS = [:occ,
12
+ :authorization_code,
13
+ :issued_at,
14
+ :amount,
15
+ :installments_amount,
16
+ :installments_number,
17
+ :buy_order].freeze
18
+ # @raise [KeyError] upon missing a response parameter
19
+ def initialize(json)
20
+ result = json.fetch('result')
21
+ @response_code = json.fetch('responseCode')
22
+ @description = json.fetch('description')
23
+ @occ = result.fetch('occ')
24
+ @authorization_code = result.fetch('authorizationCode')
25
+ @signature = result.fetch('signature')
26
+ @transaction_desc = result.fetch('transactionDesc')
27
+ @buy_order = result.fetch('buyOrder')
28
+ @issued_at = result.fetch('issuedAt')
29
+ @amount = result.fetch('amount')
30
+ @installments_amount = result.fetch('installmentsAmount')
31
+ @installments_number = result.fetch('installmentsNumber')
32
+ end
33
+
34
+ def sign(secret)
35
+ @signature = signature_for(to_data, secret)
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,32 @@
1
+ module Transbank
2
+ module Onepay
3
+ # Serializes the response to a TransactionCreateRequest
4
+ class TransactionCreateResponse
5
+ include Response
6
+
7
+ attr_accessor :occ, :ott, :external_unique_number, :qr_code_as_base64,
8
+ :issued_at, :signature
9
+
10
+ SIGNATURE_PARAMS = [:occ,
11
+ :external_unique_number,
12
+ :issued_at].freeze
13
+ # @raise [KeyError] upon trying to fetch a missing key from the response
14
+ def initialize(json)
15
+ result = json.fetch('result')
16
+ @response_code = json.fetch('responseCode')
17
+ @description = json.fetch('description')
18
+ @occ = result.fetch('occ')
19
+ @ott = result.fetch('ott')
20
+ @external_unique_number = result.fetch('externalUniqueNumber')
21
+ @qr_code_as_base64 = result.fetch('qrCodeAsBase64')
22
+ @issued_at = result.fetch('issuedAt')
23
+ @signature = result.fetch('signature')
24
+ end
25
+
26
+ def sign(secret)
27
+ @signature = signature_for(to_data, secret)
28
+ self
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,73 @@
1
+ module Transbank
2
+ module Onepay
3
+ module Utils
4
+ module JSONUtils
5
+ def self.included(mod)
6
+ # Implement #to_h if the class that includes this module doesn't have it
7
+ # implemented. Used in several model classes to make them easier to
8
+ # transform to hashes so they can be transformed to JSON afterwards
9
+ unless mod.respond_to? :to_h
10
+ mod.send(:define_method, :to_h) do
11
+ JSON.parse(self.jsonify)
12
+ end
13
+ end
14
+ end
15
+ # Get all instance variables of an instance of a class,
16
+ # then for all of these variables,
17
+ # if the instance of the class' respond_to? returns true,
18
+ # #send the variable name (so, you'll get the value of the instance variable's
19
+ # getter), then save that to a Hash where [key] is the instance variable's name
20
+ # and [value] is its value
21
+ #
22
+ # Finally, generate a JSON string from this hash
23
+ # @return [String] a JSON string created from the Hash resulting from the
24
+ # above operation
25
+ def jsonify
26
+ instance_vars = instance_variables.map! { |var| var.to_s.gsub!(/^@/, '') }
27
+ instance_as_hash =
28
+ instance_vars.reduce({}) do |resulting_hash, instance_variable|
29
+ if respond_to? instance_variable
30
+ value = send(instance_variable)
31
+ # Safe navigation operator is Ruby 2.3+
32
+ value = value.to_h if value && value.respond_to?(:to_h) unless value.is_a? Array
33
+ value = value.to_a if value && value.respond_to?(:to_a) unless value.is_a? Hash
34
+ if value.is_a? Array
35
+ value = value.map {|x| x.respond_to?(:jsonify) ? JSON.parse(x.jsonify) : x }
36
+ end
37
+
38
+ value = value.jsonify if value.respond_to? :jsonify
39
+ resulting_hash[instance_variable] = value
40
+ end
41
+ resulting_hash
42
+ end
43
+ JSON.generate instance_as_hash
44
+ end
45
+
46
+ # Receive a Hash and return a new hash same as the one we received,
47
+ # but all keys that were strings or camelCase'd are snake_case'd and
48
+ # turned into symbols.
49
+ # Example: {'camelCaseKey': "somevalue"}
50
+ # Would return: {camel_case_key: "somevalue"}
51
+ def transform_hash_keys(hash)
52
+ hash.reduce({}) do |new_hsh, (key, val)|
53
+ new_key = underscore(key).to_sym
54
+ new_hsh[new_key] = val
55
+ new_hsh
56
+ end
57
+ end
58
+
59
+ # FROM https://stackoverflow.com/a/1509957
60
+ # Transforms camelCaseWords to snake_case_words
61
+ def underscore(camel_cased_word)
62
+ camel_cased_word.to_s.gsub(/::/, '/')
63
+ .gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
64
+ .gsub(/([a-z\d])([A-Z])/,'\1_\2')
65
+ .tr("-", "_")
66
+ .downcase
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+
@@ -0,0 +1,38 @@
1
+ module Transbank
2
+ module Onepay
3
+ module Utils
4
+ module NetHelper
5
+ # POST a request to Transbank's servers, and return the parsed response
6
+ # @param uri_string [String] an URI to post to
7
+ # @param body [Hash] the body of your POST request
8
+ # @return [Hash] the JSON.parse'd response body
9
+ def http_post(uri_string, body)
10
+ uri = URI.parse(uri_string)
11
+ http = Net::HTTP.new(uri.host, uri.port)
12
+ http.use_ssl = uri.scheme == 'https'
13
+ request = Net::HTTP::Post.new(uri.path, 'Content-Type'=> 'application/json')
14
+ camel_cased_body = keys_to_camel_case(body)
15
+ request.body = JSON.generate(camel_cased_body)
16
+ result = http.request(request)
17
+ JSON.parse(result.body)
18
+ end
19
+
20
+ # Required for sending data to Transbank.
21
+ def keys_to_camel_case(hash)
22
+ hash.reduce({}) do |new_hash, (key, val)|
23
+ if val.is_a? Array
24
+ val = val.map {|value| value.is_a?(Hash) ? keys_to_camel_case(value) : value }
25
+ end
26
+ new_key = snake_to_camel_case(key.to_s)
27
+ new_hash[new_key] = val
28
+ new_hash
29
+ end
30
+ end
31
+
32
+ def snake_to_camel_case(str)
33
+ str.split('_').reduce { |string, current_word| string + current_word.capitalize }
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end