spreedly 1.4.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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,31 @@
1
+ module Spreedly
2
+
3
+ class GatewayTransaction < Transaction
4
+
5
+ field :order_id, :ip, :description, :gateway_token
6
+ field :merchant_name_descriptor, :merchant_location_descriptor
7
+ field :on_test_gateway, type: :boolean
8
+
9
+ attr_reader :response
10
+
11
+ def initialize(xml_doc)
12
+ super
13
+ @response = Response.new(xml_doc.at_xpath('.//response'))
14
+ end
15
+
16
+ end
17
+
18
+ class Response
19
+ include Fields
20
+
21
+ field :success, :pending, :cancelled, type: :boolean
22
+ field :created_at, :updated_at, type: :date_time
23
+ field :message, :avs_code, :avs_message, :cvv_code, :cvv_message, :error_code, :error_detail
24
+
25
+ def initialize(xml_doc)
26
+ initialize_fields(xml_doc)
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,7 @@
1
+ module Spreedly
2
+
3
+ class Purchase < AuthPurchase
4
+
5
+ end
6
+
7
+ end
@@ -0,0 +1,14 @@
1
+ module Spreedly
2
+
3
+ class RedactPaymentMethod < Transaction
4
+
5
+ attr_reader :payment_method
6
+
7
+ def initialize(xml_doc)
8
+ super
9
+ @payment_method = PaymentMethod.new_from(xml_doc.at_xpath('.//payment_method'))
10
+ end
11
+
12
+ end
13
+
14
+ end
@@ -0,0 +1,14 @@
1
+ module Spreedly
2
+
3
+ class Refund < 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
@@ -0,0 +1,14 @@
1
+ module Spreedly
2
+
3
+ class RetainPaymentMethod < Transaction
4
+
5
+ attr_reader :payment_method
6
+
7
+ def initialize(xml_doc)
8
+ super
9
+ @payment_method = PaymentMethod.new_from(xml_doc.at_xpath('.//payment_method'))
10
+ end
11
+
12
+ end
13
+
14
+ end
@@ -0,0 +1,41 @@
1
+
2
+ module Spreedly
3
+
4
+ class Transaction < Model
5
+
6
+ field :state, :message
7
+ field :succeeded, type: :boolean
8
+
9
+ def self.new_from(xml_doc)
10
+ case xml_doc.at_xpath('.//transaction_type').inner_text
11
+ when 'AddPaymentMethod'
12
+ return AddPaymentMethod.new(xml_doc)
13
+ when 'Purchase'
14
+ return Purchase.new(xml_doc)
15
+ when 'Authorization'
16
+ return Authorization.new(xml_doc)
17
+ when 'Capture'
18
+ return Capture.new(xml_doc)
19
+ when 'Credit'
20
+ return Refund.new(xml_doc)
21
+ when 'Void'
22
+ return Void.new(xml_doc)
23
+ when 'RetainPaymentMethod'
24
+ return RetainPaymentMethod.new(xml_doc)
25
+ when 'RedactPaymentMethod'
26
+ return RedactPaymentMethod.new(xml_doc)
27
+ else
28
+ Transaction.new(xml_doc)
29
+ end
30
+ end
31
+
32
+ def self.new_list_from(xml_doc)
33
+ transactions = xml_doc.xpath('.//transactions/transaction')
34
+ transactions.map do |each|
35
+ self.new_from(each)
36
+ end
37
+ end
38
+
39
+ end
40
+
41
+ end
@@ -0,0 +1,9 @@
1
+ module Spreedly
2
+
3
+ class Void < GatewayTransaction
4
+
5
+ field :reference_token
6
+
7
+ end
8
+
9
+ end
@@ -0,0 +1,64 @@
1
+ module Spreedly
2
+
3
+ module Urls
4
+
5
+ def base_url
6
+ "https://core.spreedly.com"
7
+ end
8
+
9
+ def find_payment_method_url(token)
10
+ "#{base_url}/v1/payment_methods/#{token}.xml"
11
+ end
12
+
13
+ def find_transaction_url(token)
14
+ "#{base_url}/v1/transactions/#{token}.xml"
15
+ end
16
+
17
+ def find_gateway_url(token)
18
+ "#{base_url}/v1/gateways/#{token}.xml"
19
+ end
20
+
21
+ def purchase_url(gateway_token)
22
+ "#{base_url}/v1/gateways/#{gateway_token}/purchase.xml"
23
+ end
24
+
25
+ def authorize_url(gateway_token)
26
+ "#{base_url}/v1/gateways/#{gateway_token}/authorize.xml"
27
+ end
28
+
29
+ def capture_url(authorization_token)
30
+ "#{base_url}/v1/transactions/#{authorization_token}/capture.xml"
31
+ end
32
+
33
+ def void_transaction_url(token)
34
+ "#{base_url}/v1/transactions/#{token}/void.xml"
35
+ end
36
+
37
+ def refund_transaction_url(token)
38
+ "#{base_url}/v1/transactions/#{token}/credit.xml"
39
+ end
40
+
41
+ def retain_payment_method_url(payment_method_token)
42
+ "#{base_url}/v1/payment_methods/#{payment_method_token}/retain.xml"
43
+ end
44
+
45
+ def redact_payment_method_url(payment_method_token)
46
+ "#{base_url}/v1/payment_methods/#{payment_method_token}/redact.xml"
47
+ end
48
+
49
+ def list_transactions_url(since_token)
50
+ since_param = "?since_token=#{since_token}" if since_token
51
+ "#{base_url}/v1/transactions.xml#{since_param}"
52
+ end
53
+
54
+ def add_gateway_url
55
+ "#{base_url}/v1/gateways.xml"
56
+ end
57
+
58
+ def add_payment_method_url
59
+ "#{base_url}/v1/payment_methods.xml"
60
+ end
61
+
62
+ end
63
+
64
+ end
@@ -1,3 +1,3 @@
1
1
  module Spreedly
2
- VERSION = "1.4.0"
2
+ VERSION = "2.0.0"
3
3
  end
data/spreedly.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'spreedly/version'
5
+
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = "spreedly"
9
+ s.version = Spreedly::VERSION
10
+ s.authors = ["Spreedly"]
11
+ s.email = ["duff@spreedly.com"]
12
+ s.summary = "Provides a Ruby wrapper for the Spreedly API."
13
+ s.description = "The Spreedly gem provides a convenient Ruby wrapper for the Spreedly API."
14
+ s.homepage = "https://github.com/spreedly/spreedly-gem"
15
+ s.license = "MIT"
16
+
17
+ s.files = `git ls-files`.split($/)
18
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_dependency 'nokogiri'
22
+
23
+ s.add_development_dependency 'bundler', '~> 1.3'
24
+ s.add_development_dependency 'rake'
25
+ s.add_development_dependency 'mocha'
26
+ s.add_development_dependency 'log_buddy'
27
+ s.add_development_dependency 'awesome_print'
28
+ end
29
+
@@ -0,0 +1,9 @@
1
+ # This file contains the Spreedly credentials to use when running remote tests.
2
+ # They're working credentials so the remote tests should simply run.
3
+
4
+ # You may instead choose to use your own credentials for your own Spreedly
5
+ # test environment by copying this file to the gitignored file named
6
+ # personal_credentials.yml located in the same directory as this file.
7
+
8
+ environment_key: "R7lHscqcYkZeDGGbthKp6GKMu15"
9
+ access_secret: "8sefxO5Q44sLWpmZpalQS3Qlqo03JbCemsqsWJR3YOLCuigOFRlaLSAn0WaL5dWU"
@@ -0,0 +1,43 @@
1
+ module Spreedly
2
+ module TestCredentials
3
+
4
+ def remote_test_environment_key
5
+ remote_creds["environment_key"]
6
+ end
7
+
8
+ def remote_test_access_secret
9
+ remote_creds["access_secret"]
10
+ end
11
+
12
+ private
13
+ def remote_creds
14
+ @@remote_creds ||= load_creds
15
+ end
16
+
17
+ def load_creds
18
+ load_default_creds.merge(load_personal_creds)
19
+ end
20
+
21
+ def load_default_creds
22
+ YAML.load(File.read(default_creds_file))
23
+ end
24
+
25
+ def load_personal_creds
26
+ return {} unless File.exists?(personal_creds_file)
27
+
28
+ personal_creds = YAML.load(File.read(personal_creds_file))
29
+ return {} unless personal_creds
30
+ raise("The file '#{personal_creds_file}' has an invalid format.\nIt should have the same format as '#{default_creds_file}'.") unless personal_creds.kind_of?(Hash)
31
+ personal_creds
32
+ end
33
+
34
+ def default_creds_file
35
+ File.join(File.dirname(__FILE__), 'credentials.yml')
36
+ end
37
+
38
+ def personal_creds_file
39
+ File.join(File.dirname(__FILE__), 'personal_credentials.yml')
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,29 @@
1
+ module Spreedly
2
+
3
+ module Assertions
4
+
5
+ def assert_raise_with_message(expected_exception_type, expected_message)
6
+ error = assert_raises(expected_exception_type) do
7
+ yield
8
+ end
9
+ assert_equal expected_message, error.message, "Exception message is incorrect"
10
+ end
11
+
12
+ def assert_invalid_login
13
+ environment = Spreedly::Environment.new("UnknownEnvironmentKey", "UnknownAccessSecret")
14
+
15
+ assert_raise_with_message(Spreedly::AuthenticationError, "Unable to authenticate using the given environment_key and access_token. Please check your credentials.") do
16
+ yield(environment)
17
+ end
18
+ end
19
+
20
+ def assert_xpaths_in(xml_doc, *xpaths)
21
+ xpaths.each do |xpath, expected_text|
22
+ assert_equal expected_text, xml_doc.xpath(xpath).text, "Looking for the text of the following xpath: #{xpath}"
23
+ end
24
+ end
25
+
26
+ end
27
+
28
+
29
+ end
@@ -0,0 +1,31 @@
1
+ module Spreedly
2
+
3
+ module CommunicationHelper
4
+
5
+ def get_request_body(response)
6
+ remembered_body = nil
7
+
8
+ @environment.define_singleton_method(:raw_ssl_request) do |method, enpoint, body, headers|
9
+ remembered_body = body
10
+ response
11
+ end
12
+
13
+ yield
14
+ Nokogiri::XML(remembered_body)
15
+ end
16
+
17
+ def get_request_url(response)
18
+ remembered_url = nil
19
+
20
+ @environment.define_singleton_method(:raw_ssl_request) do |method, endpoint, body, headers|
21
+ remembered_url = endpoint
22
+ response
23
+ end
24
+
25
+ yield
26
+ remembered_url
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,26 @@
1
+
2
+ module Spreedly
3
+
4
+ module CreationHelper
5
+
6
+ def create_card_on(environment, options = {})
7
+ deets = default_card_deets.merge(options)
8
+ environment.add_credit_card(deets).payment_method
9
+ end
10
+
11
+ def create_failed_card_on(environment, options = {})
12
+ deets = default_card_deets.merge(number: '4012888888881881').merge(options)
13
+ environment.add_credit_card(deets).payment_method
14
+ end
15
+
16
+
17
+ private
18
+ def default_card_deets
19
+ {
20
+ email: 'perrin@wot.com', number: '5555555555554444', month: 1, year: 2019,
21
+ last_name: 'Aybara', first_name: 'Perrin', retained: true
22
+ }
23
+ end
24
+ end
25
+
26
+ end
@@ -0,0 +1,18 @@
1
+ class StubResponse
2
+ attr_reader :code, :body
3
+ def self.succeeded(xml)
4
+ StubResponse.new(200, xml)
5
+ end
6
+
7
+ def self.failed(xml)
8
+ StubResponse.new(422, xml)
9
+ end
10
+
11
+ def initialize(code, body, headers={})
12
+ @code, @body, @headers = code, body, headers
13
+ end
14
+
15
+ def [](header)
16
+ @headers[header]
17
+ end
18
+ end
@@ -0,0 +1,62 @@
1
+ require 'test_helper'
2
+
3
+ class RemoteAddCreditCardTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ @environment = Spreedly::Environment.new(remote_test_environment_key, remote_test_access_secret)
7
+ end
8
+
9
+ def test_invalid_login
10
+ assert_invalid_login do |environment|
11
+ environment.add_credit_card(card_deets)
12
+ end
13
+ end
14
+
15
+ def test_failed_with_validation_errors
16
+ error = assert_raises(Spreedly::TransactionCreationError) do
17
+ @environment.add_credit_card(card_deets(last_name: '', first_name: ' '))
18
+ end
19
+
20
+ expected_errors = [
21
+ { attribute: "first_name", key: "errors.blank", message: "First name can't be blank" },
22
+ { attribute: "last_name", key: "errors.blank", message: "Last name can't be blank" }
23
+ ]
24
+
25
+ assert_equal expected_errors, error.errors
26
+ assert_equal "First name can't be blank\nLast name can't be blank", error.message
27
+ end
28
+
29
+ def test_payment_required
30
+ assert_raise_with_message(Spreedly::PaymentRequiredError, "Your account has not been activated for real transactions. Please update your subscription settings.") do
31
+ @environment.add_credit_card(card_deets(number: '343'))
32
+ end
33
+ end
34
+
35
+ def test_successful_add_card
36
+ t = @environment.add_credit_card(card_deets)
37
+
38
+ assert t.succeeded?
39
+ assert_equal 'Aybara', t.payment_method.last_name
40
+ assert !t.retained
41
+ assert_equal 'cached', t.payment_method.storage_state
42
+ assert_equal 'occupation: Blacksmith', t.payment_method.data
43
+ end
44
+
45
+ def test_successfully_retain_on_create
46
+ t = @environment.add_credit_card(card_deets(retained: true))
47
+
48
+ assert t.succeeded?
49
+ assert t.retained
50
+ assert_equal 'retained', t.payment_method.storage_state
51
+ end
52
+
53
+
54
+ private
55
+ def card_deets(options = {})
56
+ {
57
+ email: 'perrin@wot.com', number: '5555555555554444', month: 1, year: 2019,
58
+ last_name: 'Aybara', first_name: 'Perrin', data: "occupation: Blacksmith"
59
+ }.merge(options)
60
+ end
61
+
62
+ end