braintree 3.2.0 → 4.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (150) hide show
  1. checksums.yaml +4 -4
  2. data/braintree.gemspec +3 -3
  3. data/lib/braintree.rb +6 -1
  4. data/lib/braintree/account_updater_daily_report.rb +1 -1
  5. data/lib/braintree/address.rb +2 -1
  6. data/lib/braintree/apple_pay.rb +1 -1
  7. data/lib/braintree/apple_pay_card.rb +1 -1
  8. data/lib/braintree/apple_pay_options.rb +1 -1
  9. data/lib/braintree/authorization_adjustment.rb +1 -1
  10. data/lib/braintree/client_token.rb +1 -1
  11. data/lib/braintree/configuration.rb +11 -11
  12. data/lib/braintree/connected_merchant_paypal_status_changed.rb +1 -1
  13. data/lib/braintree/connected_merchant_status_transitioned.rb +1 -1
  14. data/lib/braintree/credit_card.rb +2 -2
  15. data/lib/braintree/credit_card_gateway.rb +14 -4
  16. data/lib/braintree/credit_card_verification.rb +5 -5
  17. data/lib/braintree/credit_card_verification_search.rb +1 -1
  18. data/lib/braintree/customer.rb +6 -4
  19. data/lib/braintree/customer_gateway.rb +2 -0
  20. data/lib/braintree/customer_search.rb +1 -1
  21. data/lib/braintree/disbursement.rb +1 -1
  22. data/lib/braintree/dispute.rb +15 -1
  23. data/lib/braintree/dispute/paypal_message.rb +15 -0
  24. data/lib/braintree/dispute_gateway.rb +2 -2
  25. data/lib/braintree/dispute_search.rb +3 -2
  26. data/lib/braintree/document_upload.rb +1 -1
  27. data/lib/braintree/error_codes.rb +10 -6
  28. data/lib/braintree/google_pay_card.rb +1 -1
  29. data/lib/braintree/granted_payment_instrument_update.rb +1 -1
  30. data/lib/braintree/graphql_client.rb +7 -7
  31. data/lib/braintree/http.rb +3 -3
  32. data/lib/braintree/local_payment_completed.rb +1 -1
  33. data/lib/braintree/local_payment_reversed.rb +19 -0
  34. data/lib/braintree/merchant.rb +1 -1
  35. data/lib/braintree/merchant_account.rb +1 -1
  36. data/lib/braintree/merchant_account_gateway.rb +1 -1
  37. data/lib/braintree/merchant_gateway.rb +1 -1
  38. data/lib/braintree/modification.rb +1 -1
  39. data/lib/braintree/oauth_credentials.rb +1 -1
  40. data/lib/braintree/oauth_gateway.rb +5 -5
  41. data/lib/braintree/payment_instrument_type.rb +10 -10
  42. data/lib/braintree/payment_method_gateway.rb +10 -7
  43. data/lib/braintree/payment_method_nonce.rb +7 -4
  44. data/lib/braintree/payment_method_nonce_details.rb +37 -0
  45. data/lib/braintree/payment_method_nonce_details_payer_info.rb +32 -0
  46. data/lib/braintree/payment_method_nonce_gateway.rb +1 -1
  47. data/lib/braintree/plan.rb +1 -1
  48. data/lib/braintree/processor_response_types.rb +3 -3
  49. data/lib/braintree/revoked_payment_method_metadata.rb +1 -1
  50. data/lib/braintree/risk_data.rb +3 -1
  51. data/lib/braintree/samsung_pay_card.rb +1 -1
  52. data/lib/braintree/settlement_batch_summary.rb +2 -2
  53. data/lib/braintree/subscription.rb +6 -6
  54. data/lib/braintree/three_d_secure_info.rb +22 -12
  55. data/lib/braintree/transaction.rb +32 -24
  56. data/lib/braintree/transaction_gateway.rb +24 -5
  57. data/lib/braintree/transaction_line_item.rb +1 -1
  58. data/lib/braintree/transaction_search.rb +3 -1
  59. data/lib/braintree/unknown_payment_method.rb +1 -1
  60. data/lib/braintree/us_bank_account.rb +3 -3
  61. data/lib/braintree/us_bank_account_verification.rb +1 -1
  62. data/lib/braintree/us_bank_account_verification_gateway.rb +1 -1
  63. data/lib/braintree/util.rb +4 -4
  64. data/lib/braintree/venmo_account.rb +1 -1
  65. data/lib/braintree/version.rb +1 -1
  66. data/lib/braintree/visa_checkout_card.rb +2 -2
  67. data/lib/braintree/webhook_notification.rb +30 -20
  68. data/lib/braintree/webhook_notification_gateway.rb +5 -5
  69. data/lib/braintree/webhook_testing_gateway.rb +30 -0
  70. data/lib/braintree/xml/generator.rb +5 -4
  71. data/lib/braintree/xml/libxml.rb +0 -1
  72. data/lib/braintree/xml/parser.rb +22 -12
  73. data/lib/braintree/xml/rexml.rb +70 -0
  74. data/spec/integration/braintree/add_on_spec.rb +1 -1
  75. data/spec/integration/braintree/address_spec.rb +28 -24
  76. data/spec/integration/braintree/advanced_search_spec.rb +45 -45
  77. data/spec/integration/braintree/apple_pay_spec.rb +3 -3
  78. data/spec/integration/braintree/braintree_gateway_spec.rb +2 -1
  79. data/spec/integration/braintree/client_api/client_token_spec.rb +14 -14
  80. data/spec/integration/braintree/client_api/spec_helper.rb +5 -5
  81. data/spec/integration/braintree/credit_card_spec.rb +213 -122
  82. data/spec/integration/braintree/credit_card_verification_search_spec.rb +2 -2
  83. data/spec/integration/braintree/credit_card_verification_spec.rb +1 -1
  84. data/spec/integration/braintree/customer_search_spec.rb +8 -8
  85. data/spec/integration/braintree/customer_spec.rb +282 -165
  86. data/spec/integration/braintree/dispute_search_spec.rb +28 -3
  87. data/spec/integration/braintree/dispute_spec.rb +6 -6
  88. data/spec/integration/braintree/error_codes_spec.rb +1 -1
  89. data/spec/integration/braintree/http_spec.rb +2 -2
  90. data/spec/integration/braintree/merchant_account_spec.rb +25 -26
  91. data/spec/integration/braintree/merchant_spec.rb +14 -14
  92. data/spec/integration/braintree/oauth_spec.rb +11 -11
  93. data/spec/integration/braintree/payment_method_nonce_spec.rb +28 -35
  94. data/spec/integration/braintree/payment_method_spec.rb +269 -165
  95. data/spec/integration/braintree/payment_method_us_bank_account_spec.rb +9 -9
  96. data/spec/integration/braintree/paypal_account_spec.rb +28 -28
  97. data/spec/integration/braintree/samsung_pay_card_spec.rb +9 -9
  98. data/spec/integration/braintree/settlement_batch_summary_spec.rb +8 -8
  99. data/spec/integration/braintree/subscription_spec.rb +133 -133
  100. data/spec/integration/braintree/test/transaction_amounts_spec.rb +2 -2
  101. data/spec/integration/braintree/test_transaction_spec.rb +10 -10
  102. data/spec/integration/braintree/transaction_search_spec.rb +93 -67
  103. data/spec/integration/braintree/transaction_spec.rb +574 -360
  104. data/spec/integration/braintree/transaction_us_bank_account_spec.rb +20 -20
  105. data/spec/integration/braintree/us_bank_account_spec.rb +6 -6
  106. data/spec/integration/braintree/us_bank_account_verification_search_spec.rb +7 -7
  107. data/spec/integration/braintree/us_bank_account_verification_spec.rb +8 -8
  108. data/spec/integration/braintree/visa_checkout_card_spec.rb +5 -5
  109. data/spec/integration/spec_helper.rb +9 -3
  110. data/spec/oauth_test_helper.rb +1 -1
  111. data/spec/script/httpsd.rb +6 -6
  112. data/spec/spec_helper.rb +6 -3
  113. data/spec/unit/braintree/address_spec.rb +1 -1
  114. data/spec/unit/braintree/apple_pay_card_spec.rb +1 -1
  115. data/spec/unit/braintree/client_token_spec.rb +2 -2
  116. data/spec/unit/braintree/configuration_spec.rb +42 -42
  117. data/spec/unit/braintree/credit_card_spec.rb +13 -13
  118. data/spec/unit/braintree/credit_card_verification_search_spec.rb +1 -1
  119. data/spec/unit/braintree/credit_card_verification_spec.rb +8 -4
  120. data/spec/unit/braintree/customer_spec.rb +20 -10
  121. data/spec/unit/braintree/disbursement_spec.rb +7 -7
  122. data/spec/unit/braintree/dispute_search_spec.rb +1 -0
  123. data/spec/unit/braintree/dispute_spec.rb +34 -9
  124. data/spec/unit/braintree/error_result_spec.rb +5 -5
  125. data/spec/unit/braintree/errors_spec.rb +8 -8
  126. data/spec/unit/braintree/http_spec.rb +5 -5
  127. data/spec/unit/braintree/merchant_account_spec.rb +1 -1
  128. data/spec/unit/braintree/payment_method_nonce_details_payer_info_spec.rb +31 -0
  129. data/spec/unit/braintree/payment_method_nonce_details_spec.rb +43 -0
  130. data/spec/unit/braintree/payment_method_nonce_spec.rb +40 -0
  131. data/spec/unit/braintree/payment_method_spec.rb +1 -1
  132. data/spec/unit/braintree/paypal_account_spec.rb +2 -2
  133. data/spec/unit/braintree/resource_collection_spec.rb +9 -9
  134. data/spec/unit/braintree/risk_data_spec.rb +9 -5
  135. data/spec/unit/braintree/subscription_search_spec.rb +1 -1
  136. data/spec/unit/braintree/successful_result_spec.rb +1 -1
  137. data/spec/unit/braintree/three_d_secure_info_spec.rb +32 -14
  138. data/spec/unit/braintree/transaction/credit_card_details_spec.rb +3 -3
  139. data/spec/unit/braintree/transaction/customer_details_spec.rb +1 -1
  140. data/spec/unit/braintree/transaction/deposit_details_spec.rb +2 -2
  141. data/spec/unit/braintree/transaction/paypal_details_spec.rb +1 -1
  142. data/spec/unit/braintree/transaction_search_spec.rb +12 -12
  143. data/spec/unit/braintree/transaction_spec.rb +25 -17
  144. data/spec/unit/braintree/util_spec.rb +18 -18
  145. data/spec/unit/braintree/validation_error_collection_spec.rb +36 -36
  146. data/spec/unit/braintree/webhook_notification_spec.rb +88 -56
  147. data/spec/unit/braintree/xml/rexml_spec.rb +51 -0
  148. data/spec/unit/braintree/xml_spec.rb +31 -31
  149. metadata +16 -8
  150. data/lib/braintree/settlement_batch.rb +0 -0
@@ -63,7 +63,7 @@ module Braintree
63
63
  end
64
64
 
65
65
  def self._new(*args) # :nodoc:
66
- self.new *args
66
+ self.new(*args)
67
67
  end
68
68
 
69
69
  def self.confirm_micro_transfer_amounts(*args)
@@ -18,7 +18,7 @@ module Braintree
18
18
  ErrorResult.new(@gateway, response[:api_error_response])
19
19
  else
20
20
  SuccessfulResult.new(
21
- :us_bank_account_verification => UsBankAccountVerification._new(response[:us_bank_account_verification])
21
+ :us_bank_account_verification => UsBankAccountVerification._new(response[:us_bank_account_verification]),
22
22
  )
23
23
  end
24
24
  rescue NotFoundError
@@ -14,13 +14,13 @@ module Braintree
14
14
  else
15
15
  url_encode(full_key) + "=" + url_encode(value)
16
16
  end
17
- end.sort * '&'
17
+ end.sort * "&"
18
18
  end
19
19
 
20
20
  def self.parse_query_string(qs)
21
- qs.split('&').inject({}) do |result, couplet|
22
- pair = couplet.split('=')
23
- result[CGI.unescape(pair[0]).to_sym] = CGI.unescape(pair[1] || '')
21
+ qs.split("&").inject({}) do |result, couplet|
22
+ pair = couplet.split("=")
23
+ result[CGI.unescape(pair[0]).to_sym] = CGI.unescape(pair[1] || "")
24
24
  result
25
25
  end
26
26
  end
@@ -28,7 +28,7 @@ module Braintree
28
28
  end
29
29
 
30
30
  def self._new(*args) # :nodoc:
31
- self.new *args
31
+ self.new(*args)
32
32
  end
33
33
  end
34
34
  end
@@ -1,6 +1,6 @@
1
1
  module Braintree
2
2
  module Version
3
- Major = 3
3
+ Major = 4
4
4
  Minor = 2
5
5
  Tiny = 0
6
6
 
@@ -39,7 +39,7 @@ module Braintree
39
39
  end
40
40
 
41
41
  def _most_recent_verification(attributes)
42
- verification = (attributes[:verifications] || []).sort_by{ |verification| verification[:created_at] }.reverse.first
42
+ verification = (attributes[:verifications] || []).sort_by { |verification| verification[:created_at] }.reverse.first
43
43
  CreditCardVerification._new(verification) if verification
44
44
  end
45
45
 
@@ -84,7 +84,7 @@ module Braintree
84
84
  end
85
85
 
86
86
  def self._new(*args) # :nodoc:
87
- self.new *args
87
+ self.new(*args)
88
88
  end
89
89
  end
90
90
  end
@@ -1,12 +1,17 @@
1
- require 'ostruct'
1
+ require "ostruct"
2
2
 
3
3
  module Braintree
4
4
  class WebhookNotification
5
5
  include BaseModule
6
6
 
7
7
  module Kind
8
+
9
+ AccountUpdaterDailyReport = "account_updater_daily_report"
10
+
8
11
  Check = "check"
9
12
 
13
+ ConnectedMerchantPayPalStatusChanged = "connected_merchant_paypal_status_changed"
14
+ ConnectedMerchantStatusTransitioned = "connected_merchant_status_transitioned"
10
15
  Disbursement = "disbursement"
11
16
  DisbursementException = "disbursement_exception"
12
17
 
@@ -17,6 +22,24 @@ module Braintree
17
22
  DisputeDisputed = "dispute_disputed"
18
23
  DisputeExpired = "dispute_expired"
19
24
 
25
+ GrantedPaymentInstrumentRevoked = "granted_payment_instrument_revoked"
26
+
27
+ GrantorUpdatedGrantedPaymentMethod = "grantor_updated_granted_payment_method"
28
+ GrantedPaymentMethodRevoked = "granted_payment_method_revoked"
29
+
30
+ LocalPaymentCompleted = "local_payment_completed"
31
+ LocalPaymentReversed = "local_payment_reversed"
32
+
33
+ OAuthAccessRevoked = "oauth_access_revoked"
34
+
35
+ PartnerMerchantConnected = "partner_merchant_connected"
36
+ PartnerMerchantDisconnected = "partner_merchant_disconnected"
37
+ PartnerMerchantDeclined = "partner_merchant_declined"
38
+
39
+ PaymentMethodRevokedByCustomer = "payment_method_revoked_by_customer"
40
+
41
+ RecipientUpdatedGrantedPaymentMethod = "recipient_updated_granted_payment_method"
42
+
20
43
  SubscriptionCanceled = "subscription_canceled"
21
44
  SubscriptionChargedSuccessfully = "subscription_charged_successfully"
22
45
  SubscriptionChargedUnsuccessfully = "subscription_charged_unsuccessfully"
@@ -27,25 +50,10 @@ module Braintree
27
50
 
28
51
  SubMerchantAccountApproved = "sub_merchant_account_approved"
29
52
  SubMerchantAccountDeclined = "sub_merchant_account_declined"
53
+
30
54
  TransactionDisbursed = "transaction_disbursed"
31
55
  TransactionSettlementDeclined = "transaction_settlement_declined"
32
56
  TransactionSettled = "transaction_settled"
33
- PartnerMerchantConnected = "partner_merchant_connected"
34
- PartnerMerchantDisconnected = "partner_merchant_disconnected"
35
- PartnerMerchantDeclined = "partner_merchant_declined"
36
-
37
- AccountUpdaterDailyReport = "account_updater_daily_report"
38
-
39
- OAuthAccessRevoked = "oauth_access_revoked"
40
- ConnectedMerchantStatusTransitioned = "connected_merchant_status_transitioned"
41
- ConnectedMerchantPayPalStatusChanged = "connected_merchant_paypal_status_changed"
42
-
43
- GrantorUpdatedGrantedPaymentMethod = "grantor_updated_granted_payment_method"
44
- RecipientUpdatedGrantedPaymentMethod = "recipient_updated_granted_payment_method"
45
- GrantedPaymentInstrumentRevoked = "granted_payment_instrument_revoked"
46
- PaymentMethodRevokedByCustomer = "payment_method_revoked_by_customer"
47
-
48
- LocalPaymentCompleted = "local_payment_completed"
49
57
  end
50
58
 
51
59
  attr_reader :account_updater_daily_report
@@ -57,6 +65,7 @@ module Braintree
57
65
  attr_reader :revoked_payment_method_metadata
58
66
  attr_reader :kind
59
67
  attr_reader :local_payment_completed
68
+ attr_reader :local_payment_reversed
60
69
  attr_reader :oauth_access_revocation
61
70
  attr_reader :partner_merchant
62
71
  attr_reader :source_merchant_id
@@ -87,8 +96,9 @@ module Braintree
87
96
  @connected_merchant_status_transitioned = ConnectedMerchantStatusTransitioned._new(@subject[:connected_merchant_status_transitioned]) if @subject.has_key?(:connected_merchant_status_transitioned)
88
97
  @connected_merchant_paypal_status_changed = ConnectedMerchantPayPalStatusChanged._new(@subject[:connected_merchant_paypal_status_changed]) if @subject.has_key?(:connected_merchant_paypal_status_changed)
89
98
  @granted_payment_instrument_update = GrantedPaymentInstrumentUpdate._new(@subject[:granted_payment_instrument_update]) if @subject.has_key?(:granted_payment_instrument_update)
90
- @revoked_payment_method_metadata = RevokedPaymentMethodMetadata._new(gateway, @subject) if [Kind::GrantedPaymentInstrumentRevoked, Kind::PaymentMethodRevokedByCustomer].include?(@kind)
91
- @local_payment_completed = LocalPaymentCompleted._new(@subject[:local_payment]) if @subject.has_key?(:local_payment)
99
+ @revoked_payment_method_metadata = RevokedPaymentMethodMetadata._new(gateway, @subject) if [Kind::GrantedPaymentInstrumentRevoked, Kind::PaymentMethodRevokedByCustomer, Kind::GrantedPaymentMethodRevoked].include?(@kind)
100
+ @local_payment_completed = LocalPaymentCompleted._new(@subject[:local_payment]) if @subject.has_key?(:local_payment) && Kind::LocalPaymentCompleted == @kind
101
+ @local_payment_reversed = LocalPaymentReversed._new(@subject[:local_payment_reversed]) if @subject.has_key?(:local_payment_reversed) && Kind::LocalPaymentReversed == @kind
92
102
  end
93
103
 
94
104
  def merchant_account
@@ -110,7 +120,7 @@ module Braintree
110
120
  class << self
111
121
  protected :new
112
122
  def _new(*args) # :nodoc:
113
- self.new *args
123
+ self.new(*args)
114
124
  end
115
125
  end
116
126
  end
@@ -7,8 +7,8 @@ module Braintree
7
7
  end
8
8
 
9
9
  def parse(signature_string, payload)
10
- raise InvalidSignature, 'signature cannot be nil' if signature_string.nil?
11
- raise InvalidSignature, 'payload cannot be nil' if payload.nil?
10
+ raise InvalidSignature, "signature cannot be nil" if signature_string.nil?
11
+ raise InvalidSignature, "payload cannot be nil" if payload.nil?
12
12
  if payload =~ /[^A-Za-z0-9+=\/\n]/
13
13
  raise InvalidSignature, "payload contains illegal characters"
14
14
  end
@@ -18,7 +18,7 @@ module Braintree
18
18
  end
19
19
 
20
20
  def verify(challenge)
21
- raise InvalidChallenge, 'challenge contains non-hex characters' unless challenge =~ /\A[a-f0-9]{20,32}\z/
21
+ raise InvalidChallenge, "challenge contains non-hex characters" unless challenge =~ /\A[a-f0-9]{20,32}\z/
22
22
  digest = Braintree::Digest.hexdigest(@config.private_key, challenge)
23
23
  "#{@config.public_key}|#{digest}"
24
24
  end
@@ -34,13 +34,13 @@ module Braintree
34
34
 
35
35
  def _verify_signature(signature_string, payload)
36
36
  public_key, signature = _matching_signature_pair(signature_string)
37
- raise InvalidSignature, 'no matching public key' if public_key.nil?
37
+ raise InvalidSignature, "no matching public key" if public_key.nil?
38
38
 
39
39
  signature_matches = [payload, payload + "\n"].any? do |payload|
40
40
  payload_signature = Braintree::Digest.hexdigest(@config.private_key, payload)
41
41
  Braintree::Digest.secure_compare(signature, payload_signature)
42
42
  end
43
- raise InvalidSignature, 'signature does not match payload - one has been modified' unless signature_matches
43
+ raise InvalidSignature, "signature does not match payload - one has been modified" unless signature_matches
44
44
  end
45
45
  end
46
46
  end
@@ -82,10 +82,14 @@ module Braintree
82
82
  _granted_payment_instrument_update_sample_xml(id)
83
83
  when Braintree::WebhookNotification::Kind::RecipientUpdatedGrantedPaymentMethod
84
84
  _granted_payment_instrument_update_sample_xml(id)
85
+ when Braintree::WebhookNotification::Kind::GrantedPaymentMethodRevoked
86
+ _granted_payment_method_revoked_xml(id)
85
87
  when Braintree::WebhookNotification::Kind::PaymentMethodRevokedByCustomer
86
88
  _payment_method_revoked_by_customer_sample_xml(id)
87
89
  when Braintree::WebhookNotification::Kind::LocalPaymentCompleted
88
90
  _local_payment_completed_sample_xml(id)
91
+ when Braintree::WebhookNotification::Kind::LocalPaymentReversed
92
+ _local_payment_reversed_sample_xml(id)
89
93
  else
90
94
  _subscription_sample_xml(id)
91
95
  end
@@ -875,6 +879,24 @@ module Braintree
875
879
  XML
876
880
  end
877
881
 
882
+ def _granted_payment_method_revoked_xml(id)
883
+ <<-XML
884
+ <venmo-account>
885
+ <created-at type='dateTime'>2018-10-11T21:28:37Z</created-at>
886
+ <updated-at type='dateTime'>2018-10-11T21:28:37Z</updated-at>
887
+ <default type='boolean'>true</default>
888
+ <image-url>https://assets.braintreegateway.com/payment_method_logo/venmo.png?environment=test</image-url>
889
+ <token>#{id}</token>
890
+ <source-description>Venmo Account: venmojoe</source-description>
891
+ <username>venmojoe</username>
892
+ <venmo-user-id>456</venmo-user-id>
893
+ <subscriptions type='array'/>
894
+ <customer-id>venmo_customer_id</customer-id>
895
+ <global-id>cGF5bWVudG1ldGhvZF92ZW5tb2FjY291bnQ</global-id>
896
+ </venmo-account>
897
+ XML
898
+ end
899
+
878
900
  def _payment_method_revoked_by_customer_sample_xml(id)
879
901
  <<-XML
880
902
  <paypal-account>
@@ -912,5 +934,13 @@ module Braintree
912
934
  </local-payment>
913
935
  XML
914
936
  end
937
+
938
+ def _local_payment_reversed_sample_xml(id)
939
+ <<-XML
940
+ <local-payment-reversed>
941
+ <payment-id>PAY-XYZ123</payment-id>
942
+ </local-payment-reversed>
943
+ XML
944
+ end
915
945
  end
916
946
  end
@@ -12,6 +12,7 @@ module Braintree
12
12
  "Date" => "datetime",
13
13
  "DateTime" => "datetime",
14
14
  "Time" => "datetime",
15
+ "ActiveSupport::TimeWithZone" => "datetime"
15
16
  }
16
17
 
17
18
  XML_FORMATTING_NAMES = {
@@ -25,7 +26,7 @@ module Braintree
25
26
  date_or_time.respond_to?(:xmlschema) ? date_or_time.xmlschema : date_or_time.to_s
26
27
  end,
27
28
  "bigdecimal" => Proc.new do |bigdecimal|
28
- str = bigdecimal.to_s('F')
29
+ str = bigdecimal.to_s("F")
29
30
  if str =~ /\.\d$/
30
31
  str += "0"
31
32
  end
@@ -61,15 +62,15 @@ module Braintree
61
62
  else
62
63
  type_name = XML_TYPE_NAMES[value.class.name]
63
64
 
64
- attributes = ((value.nil? || type_name.nil?) ? {} : { :type => type_name })
65
+ attributes = ((value.nil? || type_name.nil?) ? {} : {:type => type_name})
65
66
  if value.nil?
66
67
  attributes[:nil] = true
67
68
  end
68
69
 
69
- formatting_name = XML_FORMATTING_NAMES[value.class.name]
70
+ formatting_name = XML_FORMATTING_NAMES[value.class.name]
70
71
  options[:builder].tag!(_xml_escape(key),
71
72
  XML_FORMATTING[formatting_name] ? XML_FORMATTING[formatting_name].call(value) : value,
72
- attributes
73
+ attributes,
73
74
  )
74
75
  end
75
76
  end
@@ -1,4 +1,3 @@
1
- require 'libxml'
2
1
  # Portions of this code were copied and modified from Ruby on Rails, released
3
2
  # under the MIT license, copyright (c) 2005-2009 David Heinemeier Hansson
4
3
  module Braintree
@@ -2,7 +2,7 @@
2
2
  # under the MIT license, copyright (c) 2005-2009 David Heinemeier Hansson
3
3
  module Braintree
4
4
  module Xml # :nodoc:
5
- CONTENT_ROOT = '__content__'
5
+ CONTENT_ROOT = "__content__"
6
6
 
7
7
  module Parser # :nodoc:
8
8
  XML_PARSING = {
@@ -11,18 +11,28 @@ module Braintree
11
11
  "boolean" => Proc.new { |boolean| %w(1 true).include?(boolean.strip) },
12
12
  }
13
13
 
14
- def self.hash_from_xml(xml)
15
- standardized_hash_structure = ::Braintree::Xml::Libxml.parse(xml)
14
+ def self.hash_from_xml(xml, parser = _determine_parser)
15
+ standardized_hash_structure = parser.parse(xml)
16
16
  transformed_xml = _transform_xml(standardized_hash_structure)
17
17
  Util.symbolize_keys(transformed_xml)
18
18
  end
19
19
 
20
+ def self._determine_parser
21
+ # If LibXML is not available, we fall back to REXML
22
+ # This allows us to be compatible with JRuby, which LibXML does not support
23
+ if defined?(::LibXML::XML) && ::LibXML::XML.respond_to?(:default_keep_blanks=)
24
+ ::Braintree::Xml::Libxml
25
+ else
26
+ ::Braintree::Xml::Rexml
27
+ end
28
+ end
29
+
20
30
  # Transform into standard Ruby types and convert all keys to snake_case instead of dash-case
21
31
  def self._transform_xml(value)
22
32
  case value.class.to_s
23
- when 'Hash'
24
- if value['type'] == 'array'
25
- child_key, entries = value.detect { |k,v| k != 'type' } # child_key is throwaway
33
+ when "Hash"
34
+ if value["type"] == "array"
35
+ child_key, entries = value.detect { |k,v| k != "type" } # child_key is throwaway
26
36
  if entries.nil? || ((c = value[CONTENT_ROOT]) && c.strip.empty?)
27
37
  []
28
38
  else
@@ -37,21 +47,21 @@ module Braintree
37
47
  end
38
48
  elsif value.has_key?(CONTENT_ROOT)
39
49
  content = value[CONTENT_ROOT]
40
- if parser = XML_PARSING[value["type"]]
50
+ if (parser = XML_PARSING[value["type"]])
41
51
  XML_PARSING[value["type"]].call(content)
42
52
  else
43
53
  content
44
54
  end
45
- elsif value['type'] == 'string' && value['nil'] != 'true'
55
+ elsif value["type"] == "string" && value["nil"] != "true"
46
56
  ""
47
57
  elsif value == {}
48
58
  ""
49
- elsif value.nil? || value['nil'] == 'true'
59
+ elsif value.nil? || value["nil"] == "true"
50
60
  nil
51
61
  # If the type is the only element which makes it then
52
62
  # this still makes the value nil, except if type is
53
63
  # a XML node(where type['value'] is a Hash)
54
- elsif value['type'] && value.size == 1 && !value['type'].is_a?(::Hash)
64
+ elsif value["type"] && value.size == 1 && !value["type"].is_a?(::Hash)
55
65
  raise "is this needed?"
56
66
  nil
57
67
  else
@@ -61,14 +71,14 @@ module Braintree
61
71
  end
62
72
  xml_value
63
73
  end
64
- when 'Array'
74
+ when "Array"
65
75
  value.map! { |i| _transform_xml(i) }
66
76
  case value.length
67
77
  when 0 then nil
68
78
  when 1 then value.first
69
79
  else value
70
80
  end
71
- when 'String'
81
+ when "String"
72
82
  value
73
83
  else
74
84
  raise "can't transform #{value.class.name} - #{value.inspect}"
@@ -0,0 +1,70 @@
1
+ # Portions of this code were copied and modified from Ruby on Rails, released
2
+ # under the MIT license, copyright (c) 2005-2009 David Heinemeier Hansson
3
+ module Braintree
4
+ module Xml # :nodoc:
5
+ module Rexml # :nodoc:
6
+
7
+ CONTENT_KEY = "__content__".freeze
8
+
9
+ def self.parse(string)
10
+ require "rexml/document" unless defined?(REXML::Document)
11
+ doc = REXML::Document.new(string)
12
+ _merge_element!({}, doc.root)
13
+ end
14
+
15
+ def self._merge_element!(hash, element)
16
+ _merge!(hash, element.name, _collapse(element))
17
+ end
18
+
19
+ def self._collapse(element)
20
+ hash = _get_attributes(element)
21
+
22
+ if element.has_elements?
23
+ element.each_element { |child| _merge_element!(hash, child) }
24
+ _merge_texts!(hash, element) unless _empty_content?(element)
25
+ hash
26
+ else
27
+ _merge_texts!(hash, element)
28
+ end
29
+ end
30
+
31
+ def self._merge_texts!(hash, element)
32
+ unless element.has_text?
33
+ hash
34
+ else
35
+ # must use value to prevent double-escaping
36
+ _merge!(
37
+ hash,
38
+ CONTENT_KEY,
39
+ element.texts.map { |t| t.value }.join,
40
+ )
41
+ end
42
+ end
43
+
44
+ def self._merge!(hash, key, value)
45
+ if hash.has_key?(key)
46
+ if hash[key].instance_of?(Array)
47
+ hash[key] << value
48
+ else
49
+ hash[key] = [hash[key], value]
50
+ end
51
+ elsif value.instance_of?(Array)
52
+ hash[key] = [value]
53
+ else
54
+ hash[key] = value
55
+ end
56
+ hash
57
+ end
58
+
59
+ def self._get_attributes(element)
60
+ attributes = {}
61
+ element.attributes.each { |n,v| attributes[n] = v }
62
+ attributes
63
+ end
64
+
65
+ def self._empty_content?(element)
66
+ element.texts.join.strip == ""
67
+ end
68
+ end
69
+ end
70
+ end