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.
- data/.gitignore +8 -0
- data/Gemfile +4 -0
- data/HISTORY.md +5 -0
- data/README.md +362 -29
- data/Rakefile +32 -0
- data/lib/certs/cacert.pem +7815 -0
- data/lib/spreedly.rb +24 -282
- data/lib/spreedly/common/errors_parser.rb +15 -0
- data/lib/spreedly/common/fields.rb +90 -0
- data/lib/spreedly/connection.rb +40 -0
- data/lib/spreedly/environment.rb +176 -0
- data/lib/spreedly/error.rb +50 -0
- data/lib/spreedly/gateway.rb +10 -0
- data/lib/spreedly/model.rb +17 -0
- data/lib/spreedly/payment_methods/credit_card.rb +9 -0
- data/lib/spreedly/payment_methods/payment_method.rb +34 -0
- data/lib/spreedly/payment_methods/paypal.rb +7 -0
- data/lib/spreedly/payment_methods/sprel.rb +7 -0
- data/lib/spreedly/ssl_requester.rb +65 -0
- data/lib/spreedly/transactions/add_payment_method.rb +16 -0
- data/lib/spreedly/transactions/auth_purchase.rb +17 -0
- data/lib/spreedly/transactions/authorization.rb +7 -0
- data/lib/spreedly/transactions/capture.rb +14 -0
- data/lib/spreedly/transactions/gateway_transaction.rb +31 -0
- data/lib/spreedly/transactions/purchase.rb +7 -0
- data/lib/spreedly/transactions/redact_payment_method.rb +14 -0
- data/lib/spreedly/transactions/refund.rb +14 -0
- data/lib/spreedly/transactions/retain_payment_method.rb +14 -0
- data/lib/spreedly/transactions/transaction.rb +41 -0
- data/lib/spreedly/transactions/void.rb +9 -0
- data/lib/spreedly/urls.rb +64 -0
- data/lib/spreedly/version.rb +1 -1
- data/spreedly.gemspec +29 -0
- data/test/credentials/credentials.yml +9 -0
- data/test/credentials/test_credentials.rb +43 -0
- data/test/helpers/assertions.rb +29 -0
- data/test/helpers/communication_helper.rb +31 -0
- data/test/helpers/creation_helper.rb +26 -0
- data/test/helpers/stub_response.rb +18 -0
- data/test/remote/remote_add_credit_card_test.rb +62 -0
- data/test/remote/remote_add_gateway_test.rb +30 -0
- data/test/remote/remote_authorize_test.rb +48 -0
- data/test/remote/remote_capture_test.rb +71 -0
- data/test/remote/remote_find_gateway_test.rb +31 -0
- data/test/remote/remote_find_payment_method_test.rb +29 -0
- data/test/remote/remote_find_transaction_test.rb +33 -0
- data/test/remote/remote_list_transactions_test.rb +36 -0
- data/test/remote/remote_purchase_test.rb +69 -0
- data/test/remote/remote_redact_test.rb +38 -0
- data/test/remote/remote_refund_test.rb +65 -0
- data/test/remote/remote_retain_test.rb +39 -0
- data/test/remote/remote_void_test.rb +64 -0
- data/test/test_helper.rb +23 -0
- data/test/unit/add_credit_card_test.rb +74 -0
- data/test/unit/add_gateway_test.rb +26 -0
- data/test/unit/authorize_test.rb +87 -0
- data/test/unit/capture_test.rb +91 -0
- data/test/unit/environment_test.rb +18 -0
- data/test/unit/fields_test.rb +75 -0
- data/test/unit/find_gateway_test.rb +28 -0
- data/test/unit/find_payment_method_test.rb +90 -0
- data/test/unit/find_transaction_test.rb +31 -0
- data/test/unit/list_transactions_test.rb +46 -0
- data/test/unit/purchase_test.rb +95 -0
- data/test/unit/redact_payment_method_test.rb +51 -0
- data/test/unit/refund_test.rb +91 -0
- data/test/unit/response_stubs/add_credit_card_stubs.rb +43 -0
- data/test/unit/response_stubs/add_gateway_stubs.rb +39 -0
- data/test/unit/response_stubs/authorization_stubs.rb +139 -0
- data/test/unit/response_stubs/capture_stubs.rb +87 -0
- data/test/unit/response_stubs/find_gateway_stubs.rb +38 -0
- data/test/unit/response_stubs/find_payment_method_stubs.rb +108 -0
- data/test/unit/response_stubs/find_transaction_stubs.rb +43 -0
- data/test/unit/response_stubs/list_transactions_stubs.rb +110 -0
- data/test/unit/response_stubs/purchase_stubs.rb +139 -0
- data/test/unit/response_stubs/redact_payment_method_stubs.rb +54 -0
- data/test/unit/response_stubs/refund_stubs.rb +87 -0
- data/test/unit/response_stubs/retain_payment_method_stubs.rb +85 -0
- data/test/unit/response_stubs/void_stubs.rb +79 -0
- data/test/unit/retain_payment_method_test.rb +44 -0
- data/test/unit/timeout_test.rb +20 -0
- data/test/unit/void_test.rb +96 -0
- metadata +215 -29
- checksums.yaml +0 -15
- data/lib/spreedly/common.rb +0 -44
- data/lib/spreedly/mock.rb +0 -221
- 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,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,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
|
data/lib/spreedly/version.rb
CHANGED
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
|