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,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