spreedly 1.4.0 → 2.0.0

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 (87) hide show
  1. data/.gitignore +8 -0
  2. data/Gemfile +4 -0
  3. data/HISTORY.md +5 -0
  4. data/README.md +362 -29
  5. data/Rakefile +32 -0
  6. data/lib/certs/cacert.pem +7815 -0
  7. data/lib/spreedly.rb +24 -282
  8. data/lib/spreedly/common/errors_parser.rb +15 -0
  9. data/lib/spreedly/common/fields.rb +90 -0
  10. data/lib/spreedly/connection.rb +40 -0
  11. data/lib/spreedly/environment.rb +176 -0
  12. data/lib/spreedly/error.rb +50 -0
  13. data/lib/spreedly/gateway.rb +10 -0
  14. data/lib/spreedly/model.rb +17 -0
  15. data/lib/spreedly/payment_methods/credit_card.rb +9 -0
  16. data/lib/spreedly/payment_methods/payment_method.rb +34 -0
  17. data/lib/spreedly/payment_methods/paypal.rb +7 -0
  18. data/lib/spreedly/payment_methods/sprel.rb +7 -0
  19. data/lib/spreedly/ssl_requester.rb +65 -0
  20. data/lib/spreedly/transactions/add_payment_method.rb +16 -0
  21. data/lib/spreedly/transactions/auth_purchase.rb +17 -0
  22. data/lib/spreedly/transactions/authorization.rb +7 -0
  23. data/lib/spreedly/transactions/capture.rb +14 -0
  24. data/lib/spreedly/transactions/gateway_transaction.rb +31 -0
  25. data/lib/spreedly/transactions/purchase.rb +7 -0
  26. data/lib/spreedly/transactions/redact_payment_method.rb +14 -0
  27. data/lib/spreedly/transactions/refund.rb +14 -0
  28. data/lib/spreedly/transactions/retain_payment_method.rb +14 -0
  29. data/lib/spreedly/transactions/transaction.rb +41 -0
  30. data/lib/spreedly/transactions/void.rb +9 -0
  31. data/lib/spreedly/urls.rb +64 -0
  32. data/lib/spreedly/version.rb +1 -1
  33. data/spreedly.gemspec +29 -0
  34. data/test/credentials/credentials.yml +9 -0
  35. data/test/credentials/test_credentials.rb +43 -0
  36. data/test/helpers/assertions.rb +29 -0
  37. data/test/helpers/communication_helper.rb +31 -0
  38. data/test/helpers/creation_helper.rb +26 -0
  39. data/test/helpers/stub_response.rb +18 -0
  40. data/test/remote/remote_add_credit_card_test.rb +62 -0
  41. data/test/remote/remote_add_gateway_test.rb +30 -0
  42. data/test/remote/remote_authorize_test.rb +48 -0
  43. data/test/remote/remote_capture_test.rb +71 -0
  44. data/test/remote/remote_find_gateway_test.rb +31 -0
  45. data/test/remote/remote_find_payment_method_test.rb +29 -0
  46. data/test/remote/remote_find_transaction_test.rb +33 -0
  47. data/test/remote/remote_list_transactions_test.rb +36 -0
  48. data/test/remote/remote_purchase_test.rb +69 -0
  49. data/test/remote/remote_redact_test.rb +38 -0
  50. data/test/remote/remote_refund_test.rb +65 -0
  51. data/test/remote/remote_retain_test.rb +39 -0
  52. data/test/remote/remote_void_test.rb +64 -0
  53. data/test/test_helper.rb +23 -0
  54. data/test/unit/add_credit_card_test.rb +74 -0
  55. data/test/unit/add_gateway_test.rb +26 -0
  56. data/test/unit/authorize_test.rb +87 -0
  57. data/test/unit/capture_test.rb +91 -0
  58. data/test/unit/environment_test.rb +18 -0
  59. data/test/unit/fields_test.rb +75 -0
  60. data/test/unit/find_gateway_test.rb +28 -0
  61. data/test/unit/find_payment_method_test.rb +90 -0
  62. data/test/unit/find_transaction_test.rb +31 -0
  63. data/test/unit/list_transactions_test.rb +46 -0
  64. data/test/unit/purchase_test.rb +95 -0
  65. data/test/unit/redact_payment_method_test.rb +51 -0
  66. data/test/unit/refund_test.rb +91 -0
  67. data/test/unit/response_stubs/add_credit_card_stubs.rb +43 -0
  68. data/test/unit/response_stubs/add_gateway_stubs.rb +39 -0
  69. data/test/unit/response_stubs/authorization_stubs.rb +139 -0
  70. data/test/unit/response_stubs/capture_stubs.rb +87 -0
  71. data/test/unit/response_stubs/find_gateway_stubs.rb +38 -0
  72. data/test/unit/response_stubs/find_payment_method_stubs.rb +108 -0
  73. data/test/unit/response_stubs/find_transaction_stubs.rb +43 -0
  74. data/test/unit/response_stubs/list_transactions_stubs.rb +110 -0
  75. data/test/unit/response_stubs/purchase_stubs.rb +139 -0
  76. data/test/unit/response_stubs/redact_payment_method_stubs.rb +54 -0
  77. data/test/unit/response_stubs/refund_stubs.rb +87 -0
  78. data/test/unit/response_stubs/retain_payment_method_stubs.rb +85 -0
  79. data/test/unit/response_stubs/void_stubs.rb +79 -0
  80. data/test/unit/retain_payment_method_test.rb +44 -0
  81. data/test/unit/timeout_test.rb +20 -0
  82. data/test/unit/void_test.rb +96 -0
  83. metadata +215 -29
  84. checksums.yaml +0 -15
  85. data/lib/spreedly/common.rb +0 -44
  86. data/lib/spreedly/mock.rb +0 -221
  87. data/lib/spreedly/test_hacks.rb +0 -27
@@ -0,0 +1,176 @@
1
+ require 'base64'
2
+ require 'nokogiri'
3
+
4
+ module Spreedly
5
+ class Environment
6
+
7
+ include SslRequester
8
+ include Urls
9
+
10
+ attr_reader :key, :currency_code
11
+
12
+ def initialize(environment_key, access_secret, options={})
13
+ @key, @access_secret = environment_key, access_secret
14
+ @currency_code = options[:currency_code] || 'USD'
15
+ end
16
+
17
+ def transparent_redirect_form_action
18
+ "#{base_url}/v1/payment_methods"
19
+ end
20
+
21
+ def find_payment_method(token)
22
+ xml_doc = ssl_get(find_payment_method_url(token), headers)
23
+ PaymentMethod.new_from(xml_doc)
24
+ end
25
+
26
+ def find_transaction(token)
27
+ xml_doc = ssl_get(find_transaction_url(token), headers)
28
+ Transaction.new_from(xml_doc)
29
+ end
30
+
31
+ def find_gateway(token)
32
+ xml_doc = ssl_get(find_gateway_url(token), headers)
33
+ Gateway.new(xml_doc)
34
+ end
35
+
36
+ def purchase_on_gateway(gateway_token, payment_method_token, amount, options = {})
37
+ body = auth_purchase_body(amount, payment_method_token, options)
38
+ api_post(purchase_url(gateway_token), body)
39
+ end
40
+
41
+ def authorize_on_gateway(gateway_token, payment_method_token, amount, options = {})
42
+ body = auth_purchase_body(amount, payment_method_token, options)
43
+ api_post(authorize_url(gateway_token), body)
44
+ end
45
+
46
+ def capture_transaction(authorization_token, options = {})
47
+ api_post(capture_url(authorization_token), capture_body(options))
48
+ end
49
+
50
+ def void_transaction(token, options = {})
51
+ api_post(void_transaction_url(token), void_body(options))
52
+ end
53
+
54
+ def refund_transaction(token, options = {})
55
+ api_post(refund_transaction_url(token), refund_body(options))
56
+ end
57
+
58
+ def retain_payment_method(payment_method_token)
59
+ xml_doc = ssl_put(retain_payment_method_url(payment_method_token), '', headers)
60
+ Transaction.new_from(xml_doc)
61
+ end
62
+
63
+ def redact_payment_method(payment_method_token, options = {})
64
+ body = redact_payment_method_body(options)
65
+ xml_doc = ssl_put(redact_payment_method_url(payment_method_token), body, headers)
66
+ Transaction.new_from(xml_doc)
67
+ end
68
+
69
+ def list_transactions(since_token = nil)
70
+ xml_doc = ssl_get(list_transactions_url(since_token), headers)
71
+ Transaction.new_list_from(xml_doc)
72
+ end
73
+
74
+ def add_gateway(gateway_type, options = {})
75
+ body = add_gateway_body(gateway_type, options)
76
+ xml_doc = ssl_post(add_gateway_url, body, headers)
77
+ Gateway.new(xml_doc)
78
+ end
79
+
80
+ def add_credit_card(options)
81
+ api_post(add_payment_method_url, add_credit_card_body(options))
82
+ end
83
+
84
+ private
85
+ def headers
86
+ {
87
+ 'Authorization' => ('Basic ' + Base64.strict_encode64("#{@key}:#{@access_secret}").chomp),
88
+ 'Content-Type' => 'text/xml'
89
+ }
90
+ end
91
+
92
+ def auth_purchase_body(amount, payment_method_token, options)
93
+ build_xml_request('transaction') do |doc|
94
+ doc.amount amount
95
+ doc.currency_code(options[:currency_code] || currency_code)
96
+ doc.payment_method_token(payment_method_token)
97
+ add_to_doc(doc, options, :retain_on_success)
98
+ add_extra_options_for_basic_ops(doc, options)
99
+ end
100
+ end
101
+
102
+ def capture_body(options)
103
+ return '' if options.empty?
104
+
105
+ build_xml_request('transaction') do |doc|
106
+ add_to_doc(doc, options, :amount, :currency_code)
107
+ add_extra_options_for_basic_ops(doc, options)
108
+ end
109
+ end
110
+
111
+ def void_body(options)
112
+ return '' if options.empty?
113
+
114
+ build_xml_request('transaction') do |doc|
115
+ add_extra_options_for_basic_ops(doc, options)
116
+ end
117
+ end
118
+
119
+ def refund_body(options)
120
+ return '' if options.empty?
121
+
122
+ build_xml_request('transaction') do |doc|
123
+ add_to_doc(doc, options, :amount, :currency_code)
124
+ add_extra_options_for_basic_ops(doc, options)
125
+ end
126
+ end
127
+
128
+ def redact_payment_method_body(options)
129
+ return '' if options.empty?
130
+ build_xml_request('transaction') do |doc|
131
+ add_to_doc(doc, options, :remove_from_gateway)
132
+ end
133
+ end
134
+
135
+ def add_gateway_body(gateway_type, options)
136
+ build_xml_request('gateway') do |doc|
137
+ doc.gateway_type gateway_type
138
+ end
139
+ end
140
+
141
+ def add_credit_card_body(options)
142
+ build_xml_request('payment_method') do |doc|
143
+ add_to_doc(doc, options, :data, :retained, :email)
144
+ doc.credit_card do
145
+ add_to_doc(doc, options, :number, :month, :first_name, :last_name, :year,
146
+ :address1, :address2, :city, :state, :zip, :country, :phone_number)
147
+ end
148
+ end
149
+ end
150
+
151
+ def add_to_doc(doc, options, *attributes)
152
+ attributes.each do |attr|
153
+ doc.send(attr, options[attr.to_sym]) if options[attr.to_sym]
154
+ end
155
+ end
156
+
157
+ def add_extra_options_for_basic_ops(doc, options)
158
+ add_to_doc(doc, options, :order_id, :description, :ip, :merchant_name_descriptor,
159
+ :merchant_location_descriptor)
160
+ end
161
+
162
+ def build_xml_request(root)
163
+ builder = Nokogiri::XML::Builder.new
164
+ builder.__send__(root) do |doc|
165
+ yield(doc)
166
+ end
167
+ builder.to_xml
168
+ end
169
+
170
+ def api_post(url, body)
171
+ xml_doc = ssl_post(url, body, headers)
172
+ Transaction.new_from(xml_doc)
173
+ end
174
+
175
+ end
176
+ end
@@ -0,0 +1,50 @@
1
+ module Spreedly
2
+ class Error < StandardError
3
+ end
4
+
5
+ class XmlErrorsList < Error
6
+ attr_reader :errors
7
+ include ErrorsParser
8
+
9
+ def initialize(xml_doc)
10
+ @errors = errors_from(xml_doc)
11
+ end
12
+
13
+ def message
14
+ @errors.map { |each| each[:message] }.join("\n")
15
+ end
16
+ end
17
+
18
+ class AuthenticationError < XmlErrorsList
19
+ end
20
+
21
+ class NotFoundError < XmlErrorsList
22
+ end
23
+
24
+ class TransactionCreationError < XmlErrorsList
25
+ end
26
+
27
+ class PaymentRequiredError < XmlErrorsList
28
+ end
29
+
30
+ class TimeoutError < Error
31
+ def initialize(message = "The payment system is not responding.")
32
+ super
33
+ end
34
+ end
35
+
36
+ class UnexpectedResponseError < Error
37
+ attr_reader :response
38
+
39
+ def initialize(response)
40
+ @response = response
41
+ end
42
+
43
+ def to_s
44
+ "Failed with #{response.code} #{response.message if response.respond_to?(:message)}"
45
+ end
46
+ end
47
+
48
+ end
49
+
50
+
@@ -0,0 +1,10 @@
1
+
2
+ module Spreedly
3
+
4
+ class Gateway < Model
5
+
6
+ field :gateway_type, :state, :name
7
+
8
+ end
9
+
10
+ end
@@ -0,0 +1,17 @@
1
+
2
+ module Spreedly
3
+
4
+ class Model
5
+
6
+ include Fields
7
+
8
+ field :token
9
+ field :created_at, :updated_at, type: :date_time
10
+
11
+ def initialize(xml_doc)
12
+ initialize_fields(xml_doc)
13
+ end
14
+
15
+ end
16
+
17
+ end
@@ -0,0 +1,9 @@
1
+ module Spreedly
2
+
3
+ class CreditCard < PaymentMethod
4
+ field :first_name, :last_name, :full_name, :month, :year
5
+ field :number, :last_four_digits, :card_type, :verification_value
6
+ field :address1, :address2, :city, :state, :zip, :country, :phone_number
7
+ end
8
+
9
+ end
@@ -0,0 +1,34 @@
1
+ require 'time'
2
+
3
+ module Spreedly
4
+
5
+ class PaymentMethod < Model
6
+
7
+ include ErrorsParser
8
+
9
+ field :email, :storage_state, :data
10
+ attr_reader :errors
11
+
12
+ def initialize(xml_doc)
13
+ super
14
+ @errors = errors_from(xml_doc)
15
+ end
16
+
17
+ def self.new_from(xml_doc)
18
+ case xml_doc.at_xpath('.//payment_method_type').inner_text
19
+ when 'credit_card'
20
+ return CreditCard.new(xml_doc)
21
+ when 'paypal'
22
+ return Paypal.new(xml_doc)
23
+ when 'sprel'
24
+ return Sprel.new(xml_doc)
25
+ end
26
+ end
27
+
28
+ def valid?
29
+ @errors.empty?
30
+ end
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,7 @@
1
+ module Spreedly
2
+
3
+ class Paypal < PaymentMethod
4
+
5
+ end
6
+
7
+ end
@@ -0,0 +1,7 @@
1
+ module Spreedly
2
+
3
+ class Sprel < PaymentMethod
4
+
5
+ end
6
+
7
+ end
@@ -0,0 +1,65 @@
1
+ module Spreedly
2
+
3
+ module SslRequester
4
+
5
+ def ssl_get(endpoint, headers = {})
6
+ ssl_request(:get, endpoint, nil, headers)
7
+ end
8
+
9
+ def ssl_post(endpoint, body, headers = {})
10
+ ssl_request(:post, endpoint, body, headers)
11
+ end
12
+
13
+ def ssl_put(endpoint, body, headers = {})
14
+ ssl_request(:put, endpoint, body, headers)
15
+ end
16
+
17
+ private
18
+ def ssl_request(method, endpoint, body, headers)
19
+ raw_response = Timeout::timeout(70) do
20
+ raw_ssl_request(method, endpoint, body, headers)
21
+ end
22
+
23
+ show_raw_response(raw_response)
24
+ handle_response(raw_response)
25
+
26
+ rescue Timeout::Error => e
27
+ raise Spreedly::TimeoutError.new
28
+ end
29
+
30
+ def raw_ssl_request(method, endpoint, body, headers = {})
31
+ connection = Spreedly::Connection.new(endpoint)
32
+ connection.request(method, body, headers)
33
+ end
34
+
35
+ def handle_response(response)
36
+ xml_doc = Nokogiri::XML(response.body)
37
+
38
+ case response.code.to_i
39
+ when 200...300
40
+ xml_doc
41
+ when 401
42
+ raise AuthenticationError.new(xml_doc)
43
+ when 404
44
+ raise NotFoundError.new(xml_doc)
45
+ when 402
46
+ raise PaymentRequiredError.new(xml_doc)
47
+ when 422
48
+ if xml_doc.at_xpath('.//errors/error')
49
+ raise TransactionCreationError.new(xml_doc)
50
+ else
51
+ xml_doc
52
+ end
53
+ else
54
+ raise UnexpectedResponseError.new(response)
55
+ end
56
+ end
57
+
58
+ def show_raw_response(raw_response)
59
+ return unless ENV['SHOW_RAW_RESPONSE'] == 'true'
60
+ puts "\nraw_response.code: #{raw_response.code}\nraw_response.body:\n#{raw_response.body}"
61
+ end
62
+
63
+ end
64
+
65
+ end
@@ -0,0 +1,16 @@
1
+ module Spreedly
2
+
3
+ class AddPaymentMethod < Transaction
4
+
5
+ field :retained, type: :boolean
6
+
7
+ attr_reader :payment_method
8
+
9
+ def initialize(xml_doc)
10
+ super
11
+ @payment_method = PaymentMethod.new_from(xml_doc.at_xpath('.//payment_method'))
12
+ end
13
+
14
+ end
15
+
16
+ end
@@ -0,0 +1,17 @@
1
+ module Spreedly
2
+
3
+ class AuthPurchase < GatewayTransaction
4
+
5
+ field :currency_code
6
+ field :amount, type: :integer
7
+
8
+ attr_reader :payment_method
9
+
10
+ def initialize(xml_doc)
11
+ super
12
+ @payment_method = PaymentMethod.new_from(xml_doc.at_xpath('.//payment_method'))
13
+ end
14
+
15
+ end
16
+
17
+ end
@@ -0,0 +1,7 @@
1
+ module Spreedly
2
+
3
+ class Authorization < AuthPurchase
4
+
5
+ end
6
+
7
+ end
@@ -0,0 +1,14 @@
1
+ module Spreedly
2
+
3
+ class Capture < GatewayTransaction
4
+
5
+ field :currency_code, :reference_token
6
+ field :amount, type: :integer
7
+
8
+ def initialize(xml_doc)
9
+ super
10
+ end
11
+
12
+ end
13
+
14
+ end