spreedly 1.4.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|