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.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.travis.yml +8 -0
- data/CHANGELOG.md +13 -0
- data/Dockerfile +6 -0
- data/Gemfile +6 -0
- data/LICENSE.md +11 -0
- data/Makefile +24 -0
- data/README.md +87 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/docker-compose.yml +20 -0
- data/lib/transbank/sdk.rb +23 -0
- data/lib/transbank/sdk/onepay/base.rb +115 -0
- data/lib/transbank/sdk/onepay/errors/errors.rb +17 -0
- data/lib/transbank/sdk/onepay/errors/integration_type_error.rb +8 -0
- data/lib/transbank/sdk/onepay/errors/invalid_options_error.rb +8 -0
- data/lib/transbank/sdk/onepay/errors/item_error.rb +8 -0
- data/lib/transbank/sdk/onepay/errors/refund_create_error.rb +8 -0
- data/lib/transbank/sdk/onepay/errors/response_error.rb +8 -0
- data/lib/transbank/sdk/onepay/errors/shopping_cart_error.rb +8 -0
- data/lib/transbank/sdk/onepay/errors/signature_error.rb +8 -0
- data/lib/transbank/sdk/onepay/errors/transaction_commit_error.rb +8 -0
- data/lib/transbank/sdk/onepay/errors/transaction_create_error.rb +8 -0
- data/lib/transbank/sdk/onepay/errors/transbank_error.rb +9 -0
- data/lib/transbank/sdk/onepay/models/channels.rb +15 -0
- data/lib/transbank/sdk/onepay/models/item.rb +103 -0
- data/lib/transbank/sdk/onepay/models/models.rb +10 -0
- data/lib/transbank/sdk/onepay/models/refund.rb +51 -0
- data/lib/transbank/sdk/onepay/models/shopping_cart.rb +65 -0
- data/lib/transbank/sdk/onepay/models/transaction.rb +141 -0
- data/lib/transbank/sdk/onepay/requests/refund_create_request.rb +45 -0
- data/lib/transbank/sdk/onepay/requests/request.rb +18 -0
- data/lib/transbank/sdk/onepay/requests/requests.rb +9 -0
- data/lib/transbank/sdk/onepay/requests/transaction_commit_request.rb +48 -0
- data/lib/transbank/sdk/onepay/requests/transaction_create_request.rb +80 -0
- data/lib/transbank/sdk/onepay/responses/refund_create_response.rb +24 -0
- data/lib/transbank/sdk/onepay/responses/response.rb +18 -0
- data/lib/transbank/sdk/onepay/responses/responses.rb +9 -0
- data/lib/transbank/sdk/onepay/responses/transaction_commit_response.rb +39 -0
- data/lib/transbank/sdk/onepay/responses/transaction_create_response.rb +32 -0
- data/lib/transbank/sdk/onepay/utils/json_utils.rb +73 -0
- data/lib/transbank/sdk/onepay/utils/net_helper.rb +38 -0
- data/lib/transbank/sdk/onepay/utils/request_builder.rb +88 -0
- data/lib/transbank/sdk/onepay/utils/signature_utils.rb +49 -0
- data/lib/transbank/sdk/onepay/utils/utils.rb +9 -0
- data/lib/transbank/sdk/version.rb +5 -0
- data/sdk_test.sh +2 -0
- data/transbank-sdk.gemspec +33 -0
- metadata +220 -0
@@ -0,0 +1,15 @@
|
|
1
|
+
module Transbank
|
2
|
+
module Onepay
|
3
|
+
class Channel
|
4
|
+
## Contains static values for all possible channel values
|
5
|
+
|
6
|
+
WEB = 'WEB'.freeze
|
7
|
+
MOBILE = 'MOBILE'.freeze
|
8
|
+
APP = 'APP'.freeze
|
9
|
+
|
10
|
+
def self.values
|
11
|
+
constants.map { |const| const_get const }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module Transbank
|
2
|
+
module Onepay
|
3
|
+
class Item
|
4
|
+
include Utils::JSONUtils
|
5
|
+
# An Item to be purchased by the user, and to be added to a [ShoppingCart]
|
6
|
+
|
7
|
+
# @return [String] An item's description
|
8
|
+
attr_reader :description
|
9
|
+
|
10
|
+
# @return quantity [Integer] How many of units of [Item]
|
11
|
+
attr_reader :quantity
|
12
|
+
|
13
|
+
# @return amount [Integer] The value of each unit of [Item]
|
14
|
+
attr_reader :amount
|
15
|
+
|
16
|
+
# @return additional_data [String] A string with whatever additional data the
|
17
|
+
# Merchant might want to add
|
18
|
+
attr_reader :additional_data
|
19
|
+
|
20
|
+
# @return expire [Integer] Expiry for the Item
|
21
|
+
attr_reader :expire
|
22
|
+
# @param [Hash] opts options Hash
|
23
|
+
# @param description [String] The item's description
|
24
|
+
# @param quantity [Integer] How many of units of [Item]
|
25
|
+
# @param amount [Integer] The value of each unit of [Item]
|
26
|
+
# @param additional_data [String] A string with whatever additional data the
|
27
|
+
# Merchant might want to add
|
28
|
+
# @param expire [Integer] Expiry for the Item
|
29
|
+
# @raise [ItemError] when opts is not a [Hash]
|
30
|
+
def initialize(opts = {})
|
31
|
+
raise Errors::ItemError, 'Item must be a Hash' unless opts.is_a? Hash
|
32
|
+
opts = transform_hash_keys opts
|
33
|
+
self.description = opts.fetch(:description)
|
34
|
+
self.quantity = opts.fetch(:quantity)
|
35
|
+
self.amount = opts.fetch(:amount)
|
36
|
+
self.additional_data = opts.fetch(:additional_data, nil)
|
37
|
+
self.expire = opts.fetch(:expire, nil)
|
38
|
+
end
|
39
|
+
|
40
|
+
# @param description [String] An item's description
|
41
|
+
# @raise [ItemError] when description is null
|
42
|
+
def description=(description)
|
43
|
+
raise Errors::ItemError, "Description cannot be null" if description.nil?
|
44
|
+
@description = description
|
45
|
+
end
|
46
|
+
|
47
|
+
# @param quantity [Integer] How many of units of [Item]
|
48
|
+
# @raise [ItemError] when given quantity is nil or less than zero.
|
49
|
+
def quantity=(quantity)
|
50
|
+
raise Errors::ItemError, "Quantity cannot be null" if quantity.nil?
|
51
|
+
if quantity < 0
|
52
|
+
raise Errors::ItemError, "Quantity cannot be less than zero"
|
53
|
+
end
|
54
|
+
@quantity = quantity
|
55
|
+
end
|
56
|
+
|
57
|
+
# @param amount [Integer] The value of each unit of [Item]
|
58
|
+
# @raise [ItemError] when amount cannot be null.
|
59
|
+
# @raise [ArgumentError] when amount is not an Integer.
|
60
|
+
def amount=(amount)
|
61
|
+
raise Errors::ItemError, "Amount cannot be null" if amount.nil?
|
62
|
+
if amount != Integer(amount)
|
63
|
+
raise ArgumentError, "Amount is not an Integer"
|
64
|
+
end
|
65
|
+
@amount = amount
|
66
|
+
end
|
67
|
+
|
68
|
+
# @param additional_data [String] A string with whatever additional data the
|
69
|
+
# Merchant might want to add
|
70
|
+
def additional_data=(additional_data)
|
71
|
+
additional_data = '' if additional_data.nil?
|
72
|
+
@additional_data = additional_data
|
73
|
+
end
|
74
|
+
|
75
|
+
# @param expire [Integer] Expiry for the Item
|
76
|
+
def expire=(expire)
|
77
|
+
expire = expire || 0
|
78
|
+
@expire = expire
|
79
|
+
end
|
80
|
+
|
81
|
+
# Return the total amount to pay for the Item, that is, amount * quantity
|
82
|
+
# @return [Numeric] the total amount to pay for the [Item]
|
83
|
+
def total
|
84
|
+
self.quantity * self.amount
|
85
|
+
end
|
86
|
+
|
87
|
+
# Override == to allow comparison between [Item]s
|
88
|
+
# @return [boolean] true if equal, false otherwise
|
89
|
+
def ==(another_item)
|
90
|
+
self.description == another_item.description &&
|
91
|
+
self.quantity == another_item.quantity &&
|
92
|
+
self.amount == another_item.amount &&
|
93
|
+
self.additional_data == another_item.additional_data &&
|
94
|
+
self.expire == another_item.expire
|
95
|
+
end
|
96
|
+
|
97
|
+
# Alias for #==
|
98
|
+
def eql?(another_item)
|
99
|
+
self.==(another_item)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'transbank/sdk/onepay/models/item'
|
2
|
+
require 'transbank/sdk/onepay/models/channels'
|
3
|
+
require 'transbank/sdk/onepay/models/refund'
|
4
|
+
require 'transbank/sdk/onepay/models/shopping_cart'
|
5
|
+
require 'transbank/sdk/onepay/models/transaction'
|
6
|
+
|
7
|
+
module Transbank
|
8
|
+
module Onepay
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Transbank
|
2
|
+
module Onepay
|
3
|
+
class Refund
|
4
|
+
extend Utils::NetHelper, Utils::RequestBuilder
|
5
|
+
# Manages Refunds
|
6
|
+
REFUND_TRANSACTION = 'nullifytransaction'.freeze
|
7
|
+
TRANSACTION_BASE_PATH = '/ewallet-plugin-api-services/services/transactionservice/'.freeze
|
8
|
+
|
9
|
+
class << self
|
10
|
+
# Create a request for a Refund
|
11
|
+
# @param amount [Integer] Amount to be refunded. Must be the full amount of the
|
12
|
+
# [Transaction] to be refunded
|
13
|
+
# @param occ [String] Merchant purchase order
|
14
|
+
# @param external_unique_number [String] Unique identifier (per Merchant) of the [Transaction] that
|
15
|
+
# will be refunded
|
16
|
+
# @param authorization_code [String] Authorization code. This is given when the [Transaction]
|
17
|
+
# is successfully committed (with the #commit method of [Transaction])
|
18
|
+
# @param options[Hash, nil] an optional Hash with configuration overrides
|
19
|
+
# @raise [RefundCreateError] if the response is nil or has no response code
|
20
|
+
# @raise [RefundCreateError] if response from the service is not equal to 'OK'
|
21
|
+
def create(amount:, occ:, external_unique_number:, authorization_code:, options: nil)
|
22
|
+
refund_request = refund_transaction(refund_amount: amount,
|
23
|
+
occ: occ,
|
24
|
+
external_unique_number: external_unique_number,
|
25
|
+
authorization_code: authorization_code,
|
26
|
+
options: options)
|
27
|
+
response = http_post(refund_path, refund_request.to_h)
|
28
|
+
|
29
|
+
if response.nil? || !response['responseCode']
|
30
|
+
raise Errors::RefundCreateError, 'Could not obtain a response from the service.'
|
31
|
+
end
|
32
|
+
|
33
|
+
refund_create_response = RefundCreateResponse.new(response)
|
34
|
+
|
35
|
+
unless refund_create_response.response_ok?
|
36
|
+
raise Errors::RefundCreateError, refund_create_response.full_description
|
37
|
+
end
|
38
|
+
|
39
|
+
refund_create_response
|
40
|
+
end
|
41
|
+
|
42
|
+
# Return the string url to POST to
|
43
|
+
# @return [String] the url to POST to
|
44
|
+
def refund_path
|
45
|
+
Base.current_integration_type_url + TRANSACTION_BASE_PATH + REFUND_TRANSACTION
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Transbank
|
2
|
+
module Onepay
|
3
|
+
# Represents a Shopping Cart, which contains [Item]s that the user wants to buy
|
4
|
+
class ShoppingCart
|
5
|
+
include Utils::JSONUtils
|
6
|
+
|
7
|
+
# @return [Array<Item>] An [Array<Item>] with the [ShoppingCart] contents
|
8
|
+
attr_reader :items
|
9
|
+
|
10
|
+
# @param items [Array, nil] an array of Hashes that can be converted to [Item]
|
11
|
+
# if nil, an empty shopping cart is created
|
12
|
+
def initialize(items = [])
|
13
|
+
# An [Array<Item>] with the [ShoppingCart] contents
|
14
|
+
@items = []
|
15
|
+
return if items.nil? || items.empty?
|
16
|
+
|
17
|
+
items.each do |it|
|
18
|
+
it = transform_hash_keys it
|
19
|
+
item = Item.new it
|
20
|
+
self << item
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# @param item [Item] an instance of [Item]
|
25
|
+
# @return [boolean] return true if item is successfully added
|
26
|
+
def add(item)
|
27
|
+
new_total = total + item.total
|
28
|
+
if new_total < 0
|
29
|
+
raise Errors::ShoppingCartError, "Total amount cannot be less than zero."
|
30
|
+
end
|
31
|
+
@items << item
|
32
|
+
end
|
33
|
+
|
34
|
+
# Alias for #add
|
35
|
+
def << item
|
36
|
+
add item
|
37
|
+
end
|
38
|
+
|
39
|
+
# Remove an [Item] from self
|
40
|
+
# @raise [ShoppingCartError] if item is not found
|
41
|
+
def remove(item)
|
42
|
+
if @items.delete(item).nil?
|
43
|
+
raise Errors::ShoppingCartError, "Item not found"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Clear the cart, setting @items to []
|
48
|
+
def remove_all
|
49
|
+
@items = []
|
50
|
+
end
|
51
|
+
|
52
|
+
# @return [Integer] The amount in CLP of the [Item]s included in the [ShoppingCart]
|
53
|
+
def total
|
54
|
+
# Array#sum is Ruby 2.4+
|
55
|
+
@items.reduce(0) { |total, item| total + item.total }
|
56
|
+
end
|
57
|
+
|
58
|
+
# Sum the quantity of items in the cart
|
59
|
+
def items_quantity
|
60
|
+
# Array#sum is Ruby 2.4+
|
61
|
+
@items.reduce(0) { |total, item| total + item.quantity }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
module Transbank
|
2
|
+
module Onepay
|
3
|
+
## Class Transaction
|
4
|
+
# This class creates or commits a Transaction (that is, a purchase)
|
5
|
+
class Transaction
|
6
|
+
extend Utils::NetHelper, Utils::RequestBuilder
|
7
|
+
|
8
|
+
SEND_TRANSACTION = 'sendtransaction'.freeze
|
9
|
+
COMMIT_TRANSACTION = 'gettransactionnumber'.freeze
|
10
|
+
TRANSACTION_BASE_PATH = '/ewallet-plugin-api-services/services/transactionservice/'.freeze
|
11
|
+
|
12
|
+
class << self
|
13
|
+
# Create a [Transaction], initiating the purchase process.
|
14
|
+
# @param shopping_cart [ShoppingCart] contains the [Item]s to be purchased
|
15
|
+
# @param channel [String] The channel that the transaction is going to be done through. Valid values are contained on the [Transbank::Onepay::Channel] class
|
16
|
+
# @param external_unique_number [String] a unique value (per Merchant, not global) that is used to identify a Transaction
|
17
|
+
# @param options[Hash, nil] an optional Hash with configuration overrides
|
18
|
+
# @return [TransactionCreateResponse] the response to your request.
|
19
|
+
# Includes data that you will need to #commit your [Transaction]
|
20
|
+
# @raise [ShoppingCartError] if shopping cart is nil or empty
|
21
|
+
# @raise [TransactionCreateError] if channel is not valid
|
22
|
+
# @raise [TransactionCreateError] if no response is gotten, or responseCode of the response is not 'OK'
|
23
|
+
def create(shopping_cart:, channel: nil, external_unique_number: nil,
|
24
|
+
options: nil)
|
25
|
+
if is_options_hash?(channel)
|
26
|
+
options = channel
|
27
|
+
channel = nil
|
28
|
+
end
|
29
|
+
|
30
|
+
if is_options_hash?(external_unique_number)
|
31
|
+
options = external_unique_number
|
32
|
+
external_unique_number = nil
|
33
|
+
end
|
34
|
+
|
35
|
+
validate_channel!(channel)
|
36
|
+
validate_shopping_cart!(shopping_cart)
|
37
|
+
|
38
|
+
options = complete_options(options)
|
39
|
+
create_request = create_transaction(shopping_cart: shopping_cart,
|
40
|
+
channel: channel,
|
41
|
+
external_unique_number: external_unique_number,
|
42
|
+
options: options)
|
43
|
+
response = http_post(transaction_create_path, create_request.to_h)
|
44
|
+
validate_create_response!(response)
|
45
|
+
transaction_create_response = TransactionCreateResponse.new response
|
46
|
+
signature_is_valid = transaction_create_response.valid_signature?(options.fetch(:shared_secret))
|
47
|
+
unless signature_is_valid
|
48
|
+
raise Errors::SignatureError, "The response's signature is not valid."
|
49
|
+
end
|
50
|
+
transaction_create_response
|
51
|
+
end
|
52
|
+
|
53
|
+
# Commit a [Transaction]. It is MANDATORY for this to be done, and you have
|
54
|
+
# 30 seconds to do so, otherwise the [Transaction] is automatically REVERSED
|
55
|
+
# @param occ [String] Merchant purchase order
|
56
|
+
# @param external_unique_number [String] a unique value (per Merchant, not global) that is used to identify a Transaction
|
57
|
+
# @param options[Hash, nil] an optional Hash with configuration overrides
|
58
|
+
# @return [TransactionCommitResponse] The response to your commit request.
|
59
|
+
# @raise [TransactionCommitError] if response is nil or responseCode of the response is not 'OK'
|
60
|
+
def commit(occ:, external_unique_number:, options: nil)
|
61
|
+
options = complete_options(options)
|
62
|
+
commit_request = commit_transaction(occ: occ,
|
63
|
+
external_unique_number: external_unique_number,
|
64
|
+
options: options)
|
65
|
+
response = http_post(transaction_commit_path, commit_request.to_h)
|
66
|
+
validate_commit_response!(response)
|
67
|
+
transaction_commit_response = TransactionCommitResponse.new(response)
|
68
|
+
signature_is_valid = transaction_commit_response.valid_signature?(options.fetch(:shared_secret))
|
69
|
+
unless signature_is_valid
|
70
|
+
raise Errors::SignatureError, "The response's signature is not valid."
|
71
|
+
end
|
72
|
+
transaction_commit_response
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def is_options_hash?(hash)
|
78
|
+
return false unless hash.respond_to? :keys
|
79
|
+
# Intersection of the two arrays
|
80
|
+
([:api_key, :shared_secret] & hash.keys).any?
|
81
|
+
end
|
82
|
+
|
83
|
+
def validate_channel!(channel)
|
84
|
+
if channel_is_app?(channel) && Base::app_scheme.nil?
|
85
|
+
raise Errors::TransactionCreateError, 'You need to set an app_scheme if you want to use the APP channel'
|
86
|
+
end
|
87
|
+
|
88
|
+
if channel_is_mobile?(channel) && Base::callback_url.nil?
|
89
|
+
raise Errors::TransactionCreateError, 'You need to set a valid callback if you want to use the MOBILE channel'
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def validate_shopping_cart!(shopping_cart)
|
94
|
+
if shopping_cart.items.nil? || shopping_cart.items.empty?
|
95
|
+
raise Errors::ShoppingCartError, 'Shopping cart is null or empty.'
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def validate_commit_response!(response)
|
100
|
+
unless response
|
101
|
+
raise Errors::TransactionCommitError, 'Could not obtain a response from the service.'
|
102
|
+
end
|
103
|
+
|
104
|
+
unless response.fetch('responseCode') == 'OK'
|
105
|
+
msg = "#{response.fetch('responseCode')} : #{response.fetch('description')}"
|
106
|
+
raise Errors::TransactionCommitError, msg
|
107
|
+
end
|
108
|
+
response
|
109
|
+
end
|
110
|
+
|
111
|
+
def validate_create_response!(response)
|
112
|
+
unless response
|
113
|
+
raise Errors::TransactionCreateError, 'Could not obtain a response from the service.'
|
114
|
+
end
|
115
|
+
|
116
|
+
unless response.fetch('responseCode') == 'OK'
|
117
|
+
msg = "#{response.fetch('responseCode')} : #{response['description']}"
|
118
|
+
raise Errors::TransactionCreateError, msg
|
119
|
+
end
|
120
|
+
response
|
121
|
+
end
|
122
|
+
|
123
|
+
def channel_is_app?(channel)
|
124
|
+
channel && channel == Transbank::Onepay::Channel::APP
|
125
|
+
end
|
126
|
+
|
127
|
+
def channel_is_mobile?(channel)
|
128
|
+
channel && channel == Transbank::Onepay::Channel::MOBILE
|
129
|
+
end
|
130
|
+
|
131
|
+
def transaction_create_path
|
132
|
+
Base.current_integration_type_url + TRANSACTION_BASE_PATH + SEND_TRANSACTION
|
133
|
+
end
|
134
|
+
|
135
|
+
def transaction_commit_path
|
136
|
+
Base.current_integration_type_url + TRANSACTION_BASE_PATH + COMMIT_TRANSACTION
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|