transbank-sdk 1.0.1

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