braintree 2.4.0 → 2.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. data/README.rdoc +4 -0
  2. data/lib/braintree.rb +43 -32
  3. data/lib/braintree/add_on.rb +4 -0
  4. data/lib/braintree/address.rb +18 -72
  5. data/lib/braintree/address_gateway.rb +76 -0
  6. data/lib/braintree/advanced_search.rb +31 -13
  7. data/lib/braintree/base_module.rb +6 -0
  8. data/lib/braintree/configuration.rb +57 -39
  9. data/lib/braintree/credit_card.rb +75 -129
  10. data/lib/braintree/credit_card_gateway.rb +133 -0
  11. data/lib/braintree/credit_card_verification.rb +8 -0
  12. data/lib/braintree/customer.rb +70 -123
  13. data/lib/braintree/customer_gateway.rb +121 -0
  14. data/lib/braintree/digest.rb +2 -2
  15. data/lib/braintree/discount.rb +4 -0
  16. data/lib/braintree/error_codes.rb +50 -5
  17. data/lib/braintree/error_result.rb +4 -18
  18. data/lib/braintree/errors.rb +1 -2
  19. data/lib/braintree/exceptions.rb +11 -16
  20. data/lib/braintree/gateway.rb +39 -0
  21. data/lib/braintree/http.rb +30 -26
  22. data/lib/braintree/modification.rb +23 -0
  23. data/lib/braintree/resource_collection.rb +1 -1
  24. data/lib/braintree/subscription.rb +29 -129
  25. data/lib/braintree/subscription_gateway.rb +122 -0
  26. data/lib/braintree/subscription_search.rb +6 -7
  27. data/lib/braintree/successful_result.rb +1 -12
  28. data/lib/braintree/test/credit_card_numbers.rb +4 -2
  29. data/lib/braintree/test/transaction_amounts.rb +3 -0
  30. data/lib/braintree/transaction.rb +83 -243
  31. data/lib/braintree/transaction/credit_card_details.rb +4 -4
  32. data/lib/braintree/transaction_gateway.rb +124 -0
  33. data/lib/braintree/transaction_search.rb +5 -3
  34. data/lib/braintree/transparent_redirect.rb +19 -112
  35. data/lib/braintree/transparent_redirect_gateway.rb +105 -0
  36. data/lib/braintree/util.rb +4 -0
  37. data/lib/braintree/validation_error.rb +1 -0
  38. data/lib/braintree/validation_error_collection.rb +5 -23
  39. data/lib/braintree/version.rb +2 -2
  40. data/lib/braintree/xml/parser.rb +1 -1
  41. data/lib/braintree/xml/rexml.rb +2 -2
  42. data/spec/integration/braintree/advanced_search_spec.rb +532 -0
  43. data/spec/integration/braintree/credit_card_spec.rb +5 -8
  44. data/spec/integration/braintree/http_spec.rb +53 -39
  45. data/spec/integration/braintree/subscription_spec.rb +678 -213
  46. data/spec/integration/braintree/transaction_search_spec.rb +318 -43
  47. data/spec/integration/braintree/transaction_spec.rb +134 -3
  48. data/spec/integration/braintree/transparent_redirect_spec.rb +1 -1
  49. data/spec/spec_helper.rb +55 -4
  50. data/spec/unit/braintree/address_spec.rb +8 -8
  51. data/spec/unit/braintree/base_module_spec.rb +1 -1
  52. data/spec/unit/braintree/configuration_spec.rb +34 -29
  53. data/spec/unit/braintree/credit_card_spec.rb +14 -12
  54. data/spec/unit/braintree/credit_card_verification_spec.rb +16 -0
  55. data/spec/unit/braintree/customer_spec.rb +10 -8
  56. data/spec/unit/braintree/digest_spec.rb +8 -17
  57. data/spec/unit/braintree/error_result_spec.rb +12 -2
  58. data/spec/unit/braintree/http_spec.rb +2 -2
  59. data/spec/unit/braintree/subscription_search_spec.rb +77 -0
  60. data/spec/unit/braintree/subscription_spec.rb +16 -8
  61. data/spec/unit/braintree/transaction_spec.rb +17 -12
  62. data/spec/unit/braintree/transparent_redirect_spec.rb +12 -12
  63. data/spec/unit/braintree/util_spec.rb +24 -0
  64. data/spec/unit/braintree/xml/parser_spec.rb +1 -1
  65. data/spec/unit/braintree_spec.rb +1 -1
  66. metadata +16 -5
@@ -10,6 +10,10 @@ module Braintree
10
10
  set_instance_variables_from_hash attributes unless attributes.nil?
11
11
  end
12
12
 
13
+ def expiration_date
14
+ "#{expiration_month}/#{expiration_year}"
15
+ end
16
+
13
17
  def inspect
14
18
  attr_order = [:token, :bin, :last_4, :card_type, :expiration_date, :cardholder_name, :customer_location]
15
19
  formatted_attrs = attr_order.map do |attr|
@@ -18,10 +22,6 @@ module Braintree
18
22
  "#<#{formatted_attrs.join(", ")}>"
19
23
  end
20
24
 
21
- def expiration_date
22
- "#{expiration_month}/#{expiration_year}"
23
- end
24
-
25
25
  def masked_number
26
26
  "#{bin}******#{last_4}"
27
27
  end
@@ -0,0 +1,124 @@
1
+ module Braintree
2
+ class TransactionGateway # :nodoc:
3
+ def initialize(gateway)
4
+ @gateway = gateway
5
+ @config = gateway.config
6
+ end
7
+
8
+ def create(attributes)
9
+ Util.verify_keys(TransactionGateway._create_signature, attributes)
10
+ _do_create "/transactions", :transaction => attributes
11
+ end
12
+
13
+ # Deprecated
14
+ def create_from_transparent_redirect(query_string)
15
+ params = @gateway.transparent_redirect.parse_and_validate_query_string query_string
16
+ _do_create("/transactions/all/confirm_transparent_redirect_request", :id => params[:id])
17
+ end
18
+
19
+ def create_transaction_url
20
+ warn "[DEPRECATED] Transaction.create_transaction_url is deprecated. Please use TransparentRedirect.url"
21
+ "#{@config.base_merchant_url}/transactions/all/create_via_transparent_redirect_request"
22
+ end
23
+
24
+ def credit(attributes)
25
+ create(attributes.merge(:type => 'credit'))
26
+ end
27
+
28
+ def find(id)
29
+ response = @config.http.get "/transactions/#{id}"
30
+ Transaction._new(@gateway, response[:transaction])
31
+ rescue NotFoundError
32
+ raise NotFoundError, "transaction with id #{id.inspect} not found"
33
+ end
34
+
35
+ def refund(transaction_id, amount = nil)
36
+ response = @config.http.post "/transactions/#{transaction_id}/refund", :transaction => {:amount => amount}
37
+ if response[:transaction]
38
+ SuccessfulResult.new(:transaction => Transaction._new(@gateway, response[:transaction]))
39
+ elsif response[:api_error_response]
40
+ ErrorResult.new(@gateway, response[:api_error_response])
41
+ else
42
+ raise UnexpectedError, "expected :transaction or :api_error_response"
43
+ end
44
+ end
45
+
46
+ def retry_subscription_charge(subscription_id, amount=nil)
47
+ attributes = {
48
+ :amount => amount,
49
+ :subscription_id => subscription_id,
50
+ :type => Transaction::Type::Sale
51
+ }
52
+ _do_create "/transactions", :transaction => attributes
53
+ end
54
+
55
+ def sale(attributes)
56
+ create(attributes.merge(:type => 'sale'))
57
+ end
58
+
59
+ def search(&block)
60
+ search = TransactionSearch.new
61
+ block.call(search) if block
62
+
63
+ response = @config.http.post "/transactions/advanced_search_ids", {:search => search.to_hash}
64
+ ResourceCollection.new(response) { |ids| _fetch_transactions(search, ids) }
65
+ end
66
+
67
+ def submit_for_settlement(transaction_id, amount = nil)
68
+ raise ArgumentError, "transaction_id is invalid" unless transaction_id =~ /\A[0-9a-z]+\z/
69
+ response = @config.http.put "/transactions/#{transaction_id}/submit_for_settlement", :transaction => {:amount => amount}
70
+ if response[:transaction]
71
+ SuccessfulResult.new(:transaction => Transaction._new(@gateway, response[:transaction]))
72
+ elsif response[:api_error_response]
73
+ ErrorResult.new(@gateway, response[:api_error_response])
74
+ else
75
+ raise UnexpectedError, "expected :transaction or :response"
76
+ end
77
+ end
78
+
79
+ def void(transaction_id)
80
+ response = @config.http.put "/transactions/#{transaction_id}/void"
81
+ if response[:transaction]
82
+ SuccessfulResult.new(:transaction => Transaction._new(@gateway, response[:transaction]))
83
+ elsif response[:api_error_response]
84
+ ErrorResult.new(@gateway, response[:api_error_response])
85
+ else
86
+ raise UnexpectedError, "expected :transaction or :api_error_response"
87
+ end
88
+ end
89
+
90
+ def self._create_signature # :nodoc:
91
+ [
92
+ :amount, :customer_id, :merchant_account_id, :order_id, :payment_method_token, :type,
93
+ {:credit_card => [:token, :cardholder_name, :cvv, :expiration_date, :expiration_month, :expiration_year, :number]},
94
+ {:customer => [:id, :company, :email, :fax, :first_name, :last_name, :phone, :website]},
95
+ {
96
+ :billing => AddressGateway._shared_signature
97
+ },
98
+ {
99
+ :shipping => AddressGateway._shared_signature
100
+ },
101
+ {:options => [:store_in_vault, :submit_for_settlement, :add_billing_address_to_payment_method, :store_shipping_address_in_vault]},
102
+ {:custom_fields => :_any_key_}
103
+ ]
104
+ end
105
+
106
+ def _do_create(url, params=nil) # :nodoc:
107
+ response = @config.http.post url, params
108
+ if response[:transaction]
109
+ SuccessfulResult.new(:transaction => Transaction._new(@gateway, response[:transaction]))
110
+ elsif response[:api_error_response]
111
+ ErrorResult.new(@gateway, response[:api_error_response])
112
+ else
113
+ raise UnexpectedError, "expected :transaction or :api_error_response"
114
+ end
115
+ end
116
+
117
+ def _fetch_transactions(search, ids) # :nodoc:
118
+ search.ids.in ids
119
+ response = @config.http.post "/transactions/advanced_search", {:search => search.to_hash}
120
+ attributes = response[:credit_card_transactions]
121
+ Util.extract_attribute_as_array(attributes, :transaction).map { |attrs| Transaction._new(@gateway, attrs) }
122
+ end
123
+ end
124
+ end
@@ -1,6 +1,6 @@
1
1
  module Braintree
2
- class TransactionSearch < AdvancedSearch
3
- search_fields(
2
+ class TransactionSearch < AdvancedSearch # :nodoc:
3
+ text_fields(
4
4
  :billing_company,
5
5
  :billing_country_name,
6
6
  :billing_extended_address,
@@ -24,6 +24,7 @@ module Braintree
24
24
  :order_id,
25
25
  :payment_method_token,
26
26
  :processor_authorization_code,
27
+ :settlement_batch_id,
27
28
  :shipping_company,
28
29
  :shipping_country_name,
29
30
  :shipping_extended_address,
@@ -59,6 +60,7 @@ module Braintree
59
60
 
60
61
  key_value_fields :refund
61
62
 
62
- range_fields :amount, :created_at
63
+ range_fields :amount, :created_at, :authorized_at, :failed_at, :gateway_rejected_at, :processor_declined_at,
64
+ :settled_at, :submitted_for_settlement_at, :voided_at
63
65
  end
64
66
  end
@@ -1,32 +1,12 @@
1
1
  module Braintree
2
- # The TransparentRedirect module provides methods to build the tr_data param
3
- # that must be submitted when using the transparent redirect API. For more information
4
- # about transparent redirect, see (TODO).
5
- #
6
- # You must provide a redirect_url that the gateway will redirect the user to when the
7
- # action is complete.
8
- #
9
- # tr_data = Braintree::TransparentRedirect.create_customer_data(
10
- # :redirect_url => "http://example.com/redirect_back_to_merchant_site
11
- # )
12
- #
13
- # In addition to the redirect_url, any data that needs to be protected from user tampering
14
- # should be included in the tr_data. For example, to prevent the user from tampering with the transaction
15
- # amount, include the amount in the tr_data.
16
- #
17
- # tr_data = Braintree::TransparentRedirect.transaction_data(
18
- # :redirect_url => "http://example.com/complete_transaction",
19
- # :transaction => {:amount => "100.00"}
20
- # )
2
+ # See:
3
+ # * http://www.braintreepaymentsolutions.com/docs/ruby/transactions/create_tr
4
+ # * http://www.braintreepaymentsolutions.com/docs/ruby/customers/create_tr
5
+ # * http://www.braintreepaymentsolutions.com/docs/ruby/customers/update_tr
6
+ # * http://www.braintreepaymentsolutions.com/docs/ruby/credit_cards/create_tr
7
+ # * http://www.braintreepaymentsolutions.com/docs/ruby/credit_cards/update_tr
21
8
  module TransparentRedirect
22
- TransparentRedirectKeys = [:redirect_url] # :nodoc:
23
- CreateCustomerSignature = TransparentRedirectKeys + [{:customer => Customer._create_signature}] # :nodoc:
24
- UpdateCustomerSignature = TransparentRedirectKeys + [:customer_id, {:customer => Customer._update_signature}] # :nodoc:
25
- TransactionSignature = TransparentRedirectKeys + [{:transaction => Transaction._create_signature}] # :nodoc:
26
- CreateCreditCardSignature = TransparentRedirectKeys + [{:credit_card => CreditCard._create_signature}] # :nodoc:
27
- UpdateCreditCardSignature = TransparentRedirectKeys + [:payment_method_token, {:credit_card => CreditCard._update_signature}] # :nodoc:
28
-
29
- module Kind
9
+ module Kind # :nodoc:
30
10
  CreateCustomer = "create_customer"
31
11
  UpdateCustomer = "update_customer"
32
12
  CreatePaymentMethod = "create_payment_method"
@@ -35,110 +15,37 @@ module Braintree
35
15
  end
36
16
 
37
17
  def self.confirm(query_string)
38
- params = TransparentRedirect.parse_and_validate_query_string query_string
39
- confirmation_klass = {
40
- Kind::CreateCustomer => Braintree::Customer,
41
- Kind::UpdateCustomer => Braintree::Customer,
42
- Kind::CreatePaymentMethod => Braintree::CreditCard,
43
- Kind::UpdatePaymentMethod => Braintree::CreditCard,
44
- Kind::CreateTransaction => Braintree::Transaction
45
- }[params[:kind]]
46
-
47
- confirmation_klass._do_create("/transparent_redirect_requests/#{params[:id]}/confirm")
18
+ Configuration.gateway.transparent_redirect.confirm(query_string)
48
19
  end
49
20
 
50
- # Returns the tr_data string for creating a credit card.
21
+ # See http://www.braintreepaymentsolutions.com/docs/ruby/credit_cards/create_tr
51
22
  def self.create_credit_card_data(params)
52
- Util.verify_keys(CreateCreditCardSignature, params)
53
- params[:kind] = Kind::CreatePaymentMethod
54
- _data(params)
23
+ Configuration.gateway.transparent_redirect.create_credit_card_data(params)
55
24
  end
56
25
 
57
- # Returns the tr_data string for creating a customer.
26
+ # See http://www.braintreepaymentsolutions.com/docs/ruby/customers/create_tr
58
27
  def self.create_customer_data(params)
59
- Util.verify_keys(CreateCustomerSignature, params)
60
- params[:kind] = Kind::CreateCustomer
61
- _data(params)
62
- end
63
-
64
- def self.parse_and_validate_query_string(query_string) # :nodoc:
65
- params = Util.symbolize_keys(Util.parse_query_string(query_string))
66
- query_string_without_hash = query_string[/(.*)&hash=.*/, 1]
67
-
68
- if params[:http_status] == nil
69
- raise UnexpectedError, "expected query string to have an http_status param"
70
- elsif params[:http_status] != '200'
71
- Util.raise_exception_for_status_code(params[:http_status], params[:bt_message])
72
- end
73
-
74
- if _hash(query_string_without_hash) == params[:hash]
75
- params
76
- else
77
- raise ForgedQueryString
78
- end
28
+ Configuration.gateway.transparent_redirect.create_customer_data(params)
79
29
  end
80
30
 
81
- # Returns the tr_data string for creating a transaction.
31
+ # See http://www.braintreepaymentsolutions.com/docs/ruby/transactions/create_tr
82
32
  def self.transaction_data(params)
83
- Util.verify_keys(TransactionSignature, params)
84
- params[:kind] = Kind::CreateTransaction
85
- transaction_type = params[:transaction] && params[:transaction][:type]
86
- unless %w[sale credit].include?(transaction_type)
87
- raise ArgumentError, "expected transaction[type] of sale or credit, was: #{transaction_type.inspect}"
88
- end
89
- _data(params)
33
+ Configuration.gateway.transparent_redirect.transaction_data(params)
90
34
  end
91
35
 
92
- # Returns the tr_data string for updating a credit card.
93
- # The payment_method_token of the credit card to update is required.
94
- #
95
- # tr_data = Braintree::TransparentRedirect.update_credit_card_data(
96
- # :redirect_url => "http://example.com/redirect_here",
97
- # :payment_method_token => "token123"
98
- # )
36
+ # See http://www.braintreepaymentsolutions.com/docs/ruby/credit_cards/update_tr
99
37
  def self.update_credit_card_data(params)
100
- Util.verify_keys(UpdateCreditCardSignature, params)
101
- unless params[:payment_method_token]
102
- raise ArgumentError, "expected params to contain :payment_method_token of payment method to update"
103
- end
104
- params[:kind] = Kind::UpdatePaymentMethod
105
- _data(params)
38
+ Configuration.gateway.transparent_redirect.update_credit_card_data(params)
106
39
  end
107
40
 
108
- # Returns the tr_data string for updating a customer.
109
- # The customer_id of the customer to update is required.
110
- #
111
- # tr_data = Braintree::TransparentRedirect.update_customer_data(
112
- # :redirect_url => "http://example.com/redirect_here",
113
- # :customer_id => "customer123"
114
- # )
41
+ # See http://www.braintreepaymentsolutions.com/docs/ruby/customers/update_tr
115
42
  def self.update_customer_data(params)
116
- Util.verify_keys(UpdateCustomerSignature, params)
117
- unless params[:customer_id]
118
- raise ArgumentError, "expected params to contain :customer_id of customer to update"
119
- end
120
- params[:kind] = Kind::UpdateCustomer
121
- _data(params)
43
+ Configuration.gateway.transparent_redirect.update_customer_data(params)
122
44
  end
123
45
 
124
46
  # Returns the URL to which Transparent Redirect Requests should be posted
125
47
  def self.url
126
- "#{Braintree::Configuration.base_merchant_url}/transparent_redirect_requests"
127
- end
128
-
129
- def self._data(params) # :nodoc:
130
- raise ArgumentError, "expected params to contain :redirect_url" unless params[:redirect_url]
131
- tr_data_segment = Util.hash_to_query_string(params.merge(
132
- :api_version => Configuration::API_VERSION,
133
- :time => Time.now.utc.strftime("%Y%m%d%H%M%S"),
134
- :public_key => Configuration.public_key
135
- ))
136
- tr_data_hash = _hash(tr_data_segment)
137
- "#{tr_data_hash}|#{tr_data_segment}"
138
- end
139
-
140
- def self._hash(string) # :nodoc:
141
- ::Braintree::Digest.hexdigest(string)
48
+ Configuration.gateway.transparent_redirect.url
142
49
  end
143
50
  end
144
51
  end
@@ -0,0 +1,105 @@
1
+ module Braintree
2
+ class TransparentRedirectGateway # :nodoc
3
+ TransparentRedirectKeys = [:redirect_url] # :nodoc:
4
+ CreateCustomerSignature = TransparentRedirectKeys + [{:customer => CustomerGateway._create_signature}] # :nodoc:
5
+ UpdateCustomerSignature = TransparentRedirectKeys + [:customer_id, {:customer => CustomerGateway._update_signature}] # :nodoc:
6
+ TransactionSignature = TransparentRedirectKeys + [{:transaction => TransactionGateway._create_signature}] # :nodoc:
7
+ CreateCreditCardSignature = TransparentRedirectKeys + [{:credit_card => CreditCardGateway._create_signature}] # :nodoc:
8
+ UpdateCreditCardSignature = TransparentRedirectKeys + [:payment_method_token, {:credit_card => CreditCardGateway._update_signature}] # :nodoc:
9
+
10
+ def initialize(gateway)
11
+ @gateway = gateway
12
+ @config = gateway.config
13
+ end
14
+
15
+ def confirm(query_string)
16
+ params = @gateway.transparent_redirect.parse_and_validate_query_string query_string
17
+ confirmation_gateway = {
18
+ TransparentRedirect::Kind::CreateCustomer => :customer,
19
+ TransparentRedirect::Kind::UpdateCustomer => :customer,
20
+ TransparentRedirect::Kind::CreatePaymentMethod => :credit_card,
21
+ TransparentRedirect::Kind::UpdatePaymentMethod => :credit_card,
22
+ TransparentRedirect::Kind::CreateTransaction => :transaction
23
+ }[params[:kind]]
24
+
25
+ @gateway.send(confirmation_gateway)._do_create("/transparent_redirect_requests/#{params[:id]}/confirm")
26
+ end
27
+
28
+ def create_credit_card_data(params)
29
+ Util.verify_keys(CreateCreditCardSignature, params)
30
+ params[:kind] = TransparentRedirect::Kind::CreatePaymentMethod
31
+ _data(params)
32
+ end
33
+
34
+ def create_customer_data(params)
35
+ Util.verify_keys(CreateCustomerSignature, params)
36
+ params[:kind] = TransparentRedirect::Kind::CreateCustomer
37
+ _data(params)
38
+ end
39
+
40
+ def parse_and_validate_query_string(query_string) # :nodoc:
41
+ params = Util.symbolize_keys(Util.parse_query_string(query_string))
42
+ query_string_without_hash = query_string[/(.*)&hash=.*/, 1]
43
+
44
+ if params[:http_status] == nil
45
+ raise UnexpectedError, "expected query string to have an http_status param"
46
+ elsif params[:http_status] != '200'
47
+ Util.raise_exception_for_status_code(params[:http_status], params[:bt_message])
48
+ end
49
+
50
+ if _hash(query_string_without_hash) == params[:hash]
51
+ params
52
+ else
53
+ raise ForgedQueryString
54
+ end
55
+ end
56
+
57
+ def transaction_data(params)
58
+ Util.verify_keys(TransactionSignature, params)
59
+ params[:kind] = TransparentRedirect::Kind::CreateTransaction
60
+ transaction_type = params[:transaction] && params[:transaction][:type]
61
+ unless %w[sale credit].include?(transaction_type)
62
+ raise ArgumentError, "expected transaction[type] of sale or credit, was: #{transaction_type.inspect}"
63
+ end
64
+ _data(params)
65
+ end
66
+
67
+ def update_credit_card_data(params)
68
+ Util.verify_keys(UpdateCreditCardSignature, params)
69
+ unless params[:payment_method_token]
70
+ raise ArgumentError, "expected params to contain :payment_method_token of payment method to update"
71
+ end
72
+ params[:kind] = TransparentRedirect::Kind::UpdatePaymentMethod
73
+ _data(params)
74
+ end
75
+
76
+ def update_customer_data(params)
77
+ Util.verify_keys(UpdateCustomerSignature, params)
78
+ unless params[:customer_id]
79
+ raise ArgumentError, "expected params to contain :customer_id of customer to update"
80
+ end
81
+ params[:kind] = TransparentRedirect::Kind::UpdateCustomer
82
+ _data(params)
83
+ end
84
+
85
+ def url
86
+ "#{@config.base_merchant_url}/transparent_redirect_requests"
87
+ end
88
+
89
+ def _data(params) # :nodoc:
90
+ raise ArgumentError, "expected params to contain :redirect_url" unless params[:redirect_url]
91
+ tr_data_segment = Util.hash_to_query_string(params.merge(
92
+ :api_version => @config.api_version,
93
+ :time => Time.now.utc.strftime("%Y%m%d%H%M%S"),
94
+ :public_key => @config.public_key
95
+ ))
96
+ tr_data_hash = _hash(tr_data_segment)
97
+ "#{tr_data_hash}|#{tr_data_segment}"
98
+ end
99
+
100
+ def _hash(string) # :nodoc:
101
+ ::Braintree::Digest.hexdigest(@config.private_key, string)
102
+ end
103
+ end
104
+ end
105
+
@@ -104,6 +104,10 @@ module Braintree
104
104
  full_key = (namespace ? "#{namespace}[#{key}]" : key.to_s)
105
105
  if value.is_a?(Hash)
106
106
  result += _flatten_hash_keys(value, full_key)
107
+ elsif value.is_a?(Array)
108
+ value.each do |item|
109
+ result += _flatten_hash_keys(item, full_key)
110
+ end
107
111
  else
108
112
  result << full_key
109
113
  end