laundry 0.0.5 → 0.0.6

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 CHANGED
@@ -15,4 +15,6 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
- .DS_Store
18
+ .DS_Store
19
+ *.sublime-workspace
20
+ *.sw*
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Have you ever wanted to use [ACH Direct](http://www.achdirect.com)'s [Payments Gateway](http://paymentsgateway.com) SOAP API? Neither did anyone. However, with this little gem you should be able to interact with it without going too terribly nuts.
4
4
 
5
- The basic idea is to have a somewhat ActiveRecord-y syntax to making payments, updating client information, etc.
5
+ The goal is to have a lightweight ActiveRecord-ish syntax to making payments, updating client information, etc.
6
6
 
7
7
  [View the Rdoc](http://rdoc.info/github/supapuerco/laundry/master/frames)
8
8
 
@@ -30,43 +30,71 @@ Or install it yourself as:
30
30
 
31
31
  ## Usage
32
32
 
33
- First, set up your merchant **account details**, possibly in a Rails initializer. (You're not limited to this singleton thing, but it's pretty useful if you just have one account.)
33
+ ### Merchant Setup
34
+
35
+ As a user of Payments Gateway's API, you probably have a *merchant account*, which serves as the context for all your transactions.
36
+
37
+ The first thing will be to **enter your api key details**:
34
38
 
35
39
  ```ruby
36
- Laundry::PaymentsGateway::Merchant.default_merchant({
37
- id: '123456',
38
- api_login_id: 'abc123',
39
- api_password: 'secretsauce',
40
+ merchant = Laundry::PaymentsGateway::Merchant.new({
41
+ id: '123456',
42
+ api_login_id: 'abc123',
43
+ api_password: 'secretsauce',
40
44
  transaction_password: 'moneymoneymoney'
41
45
  })
42
46
  ```
43
47
 
48
+ Since you probably just have the one account you can store that by using `default_merchant` instead of `new`:
49
+
50
+ ```ruby
51
+ Laundry::PaymentsGateway::Merchant.default_merchant({...})
52
+ ```
53
+
54
+ Then, you can access your merchant without having to keep track of the account details by calling `Laundry::PaymentsGateway::Merchant.default_merchant` again.
55
+
56
+ ### Sandbox
57
+
44
58
  In development? You should probably **sandbox** this baby:
45
59
 
46
60
  ```ruby
47
61
  Laundry.sandboxed = !Rails.env.production?
48
62
  ```
49
63
 
64
+ ### The Good Stuff
65
+
50
66
  Then you can **find a client**:
51
67
 
52
68
  ```ruby
53
- client = Laundry::PaymentsGateway::Merchant.default_merchant.clients.find(10)
69
+ client = merchant.clients.find(10)
54
70
  ```
55
71
 
56
72
  Create a **bank account**:
57
73
 
58
74
  ```ruby
59
- client.accounts.create!({
75
+ account_id = client.accounts.create!({
60
76
  acct_holder_name: user.name,
61
- ec_account_number: '12345678912345689',
77
+ ec_account_number: '12345678912345689',
62
78
  ec_account_trn: '123457890',
63
79
  ec_account_type: "CHECKING"
64
80
  })
65
81
  ```
66
82
 
67
- **Send some money**:
83
+ Or find an existing one:
84
+
85
+ ```ruby
86
+ account = client.accounts.find(1234)
87
+ ```
88
+
89
+ And, of course, **Send some money**:
90
+ ```ruby
91
+ account.credit_cents 1250
92
+ ```
93
+
94
+ Or take it:
95
+
68
96
  ```ruby
69
- account.debit_cents 1250
97
+ account.debit_cents 20000
70
98
  ```
71
99
 
72
100
  ## Contributing
data/Rakefile CHANGED
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env rake
2
2
  require "bundler/gem_tasks"
3
+ require 'rubygems'
4
+ require 'rake'
5
+ require 'rspec/core/rake_task'
3
6
 
4
- desc "Open an irb session preloaded with this library"
5
- task :console do
6
- sh "bundle exec irb -rubygems -I lib -r laundry.rb"
7
+ desc "Run RSpec"
8
+ RSpec::Core::RakeTask.new do |t|
9
+ t.verbose = false
7
10
  end
8
11
 
9
- task :default => [:travis]
10
- task :travis do
11
-
12
- end
12
+ task :default => :spec
data/laundry.gemspec CHANGED
@@ -2,7 +2,7 @@
2
2
  require File.expand_path('../lib/laundry/version', __FILE__)
3
3
 
4
4
  Gem::Specification.new do |gem|
5
-
5
+
6
6
  gem.authors = ["Wil Gieseler"]
7
7
  gem.email = ["supapuerco@gmail.com"]
8
8
  gem.description = "A soapy interface to ACH Direct's PaymentsGateway.com service."
@@ -15,7 +15,11 @@ Gem::Specification.new do |gem|
15
15
  gem.name = "laundry"
16
16
  gem.require_paths = ["lib"]
17
17
  gem.version = Laundry::VERSION
18
-
18
+
19
19
  gem.add_dependency 'savon'
20
-
20
+
21
+ gem.add_development_dependency 'rspec'
22
+ gem.add_development_dependency 'factory_girl'
23
+ gem.add_development_dependency 'debugger'
24
+
21
25
  end
@@ -0,0 +1,15 @@
1
+ {
2
+ "folders":
3
+ [
4
+ {
5
+ "path": "."
6
+ }
7
+ ],
8
+ "settings":
9
+ {
10
+ "tab_size": 2,
11
+ "translate_tabs_to_spaces": false,
12
+ "detect_indentation": false,
13
+ "trim_trailing_white_space_on_save": true
14
+ }
15
+ }
data/lib/laundry.rb CHANGED
@@ -3,27 +3,35 @@ require 'savon'
3
3
 
4
4
  # Don't log Laundry xmls to STDOUT.
5
5
  Savon.configure do |config|
6
- config.log = false
7
- config.log_level = :error
8
- HTTPI.log = false
6
+ config.log = false
7
+ config.log_level = :error
8
+ HTTPI.log = false
9
9
  end
10
10
 
11
11
  module Laundry
12
-
12
+
13
13
  @@sandboxed = true
14
14
 
15
15
  def self.sandboxed?
16
16
  @@sandboxed
17
17
  end
18
-
18
+
19
19
  def self.sandboxed=(yn)
20
20
  @@sandboxed = yn
21
21
  end
22
22
 
23
+ def self.stub!
24
+ stub_all unless require "laundry/stubbed"
25
+ end
26
+
27
+ def self.stubbed?
28
+ false
29
+ end
30
+
23
31
  end
24
32
 
25
33
  # Lib
26
34
  require "laundry/lib/soap_model"
27
35
 
28
36
  # Payments Gateway
29
- require "laundry/payments_gateway/payments_gateway"
37
+ require "laundry/payments_gateway/payments_gateway"
@@ -10,101 +10,105 @@ module Laundry
10
10
  end
11
11
 
12
12
  def setup
13
- class_action_module
14
- instance_action_module
13
+ class_action_module
14
+ instance_action_module
15
+ end
16
+
17
+ # Accepts one or more SOAP actions and generates both class and instance methods named
18
+ # after the given actions. Each generated method accepts an optional SOAP body Hash and
19
+ # a block to be passed to <tt>Savon::Client#request</tt> and executes a SOAP request.
20
+ def actions(*actions)
21
+ actions.each do |action|
22
+ define_class_action(action)
23
+ define_instance_action(action)
15
24
  end
25
+ end
16
26
 
17
- # Accepts one or more SOAP actions and generates both class and instance methods named
18
- # after the given actions. Each generated method accepts an optional SOAP body Hash and
19
- # a block to be passed to <tt>Savon::Client#request</tt> and executes a SOAP request.
20
- def actions(*actions)
21
- actions.each do |action|
22
- define_class_action(action)
23
- define_instance_action(action)
27
+ private
28
+
29
+ # Defines a class-level SOAP action method.
30
+ def define_class_action(action)
31
+ class_action_module.module_eval %{
32
+ def #{action.to_s.snakecase}(body = nil, &block)
33
+ client_request #{action.inspect}, :body => body, &block
24
34
  end
25
- end
35
+ }
36
+ end
26
37
 
27
- private
38
+ # Defines an instance-level SOAP action method.
39
+ def define_instance_action(action)
40
+ instance_action_module.module_eval %{
41
+ def #{action.to_s.snakecase}(body = nil, &block)
42
+ self.class.#{action.to_s.snakecase} merged_default_body(body), &block
43
+ end
44
+ }
45
+ end
28
46
 
29
- # Defines a class-level SOAP action method.
30
- def define_class_action(action)
31
- class_action_module.module_eval %{
32
- def #{action.to_s.snakecase}(body = nil, &block)
33
- client.request :wsdl, #{action.inspect}, :body => body, &block
34
- end
35
- }
36
- end
47
+ # Class methods.
48
+ def class_action_module
49
+ @class_action_module ||= Module.new do
37
50
 
38
- # Defines an instance-level SOAP action method.
39
- def define_instance_action(action)
40
- instance_action_module.module_eval %{
41
- def #{action.to_s.snakecase}(body = nil, &block)
42
- self.class.#{action.to_s.snakecase} merged_default_body(body), &block
43
- end
44
- }
45
- end
46
-
47
- # Class methods.
48
- def class_action_module
49
- @class_action_module ||= Module.new do
50
-
51
- # Returns the memoized <tt>Savon::Client</tt>.
52
- def client(&block)
53
- @client ||= Savon::Client.new(&block)
54
- end
55
-
56
- # Sets the SOAP endpoint to the given +uri+.
57
- def endpoint(uri)
58
- client.wsdl.endpoint = uri
59
- end
60
-
61
- # Sets the target namespace.
62
- def namespace(uri)
63
- client.wsdl.namespace = uri
64
- end
65
-
66
- # Sets the WSDL document to the given +uri+.
67
- def document(uri)
68
- client.wsdl.document = uri
69
- end
70
-
71
- # Sets the HTTP headers.
72
- def headers(headers)
73
- client.http.headers = headers
74
- end
75
-
76
- # Sets basic auth +login+ and +password+.
77
- def basic_auth(login, password)
78
- client.http.auth.basic(login, password)
79
- end
80
-
81
- # Sets WSSE auth credentials.
82
- def wsse_auth(*args)
83
- client.wsse.credentials(*args)
84
- end
85
-
86
- end.tap { |mod| extend(mod) }
87
- end
51
+ # Returns the memoized <tt>Savon::Client</tt>.
52
+ def client(&block)
53
+ @client ||= Savon::Client.new(&block)
54
+ end
88
55
 
89
- # Instance methods.
90
- def instance_action_module
91
- @instance_action_module ||= Module.new do
92
-
93
- # Returns the <tt>Savon::Client</tt> from the class instance.
94
- def client(&block)
95
- self.class.client(&block)
96
- end
97
-
98
- private
99
-
100
- def merged_default_body(body = {})
101
- default = self.respond_to?(:default_body) ? self.default_body : nil
102
- (default || {}).merge (body || {})
103
- end
104
-
105
- end.tap { |mod| include(mod) }
106
- end
56
+ # Sets the SOAP endpoint to the given +uri+.
57
+ def endpoint(uri)
58
+ client.wsdl.endpoint = uri
59
+ end
60
+
61
+ # Sets the target namespace.
62
+ def namespace(uri)
63
+ client.wsdl.namespace = uri
64
+ end
65
+
66
+ # Sets the WSDL document to the given +uri+.
67
+ def document(uri)
68
+ client.wsdl.document = uri
69
+ end
107
70
 
71
+ # Sets the HTTP headers.
72
+ def headers(headers)
73
+ client.http.headers = headers
74
+ end
75
+
76
+ def client_request(action, body, &block)
77
+ client.request :wsdl, action, body, &block
78
+ end
79
+
80
+ # Sets basic auth +login+ and +password+.
81
+ def basic_auth(login, password)
82
+ client.http.auth.basic(login, password)
83
+ end
84
+
85
+ # Sets WSSE auth credentials.
86
+ def wsse_auth(*args)
87
+ client.wsse.credentials(*args)
88
+ end
89
+
90
+ end.tap { |mod| extend(mod) }
108
91
  end
109
92
 
93
+ # Instance methods.
94
+ def instance_action_module
95
+ @instance_action_module ||= Module.new do
96
+
97
+ # Returns the <tt>Savon::Client</tt> from the class instance.
98
+ def client(&block)
99
+ self.class.client(&block)
100
+ end
101
+
102
+ private
103
+
104
+ def merged_default_body(body = {})
105
+ default = self.respond_to?(:default_body) ? self.default_body : nil
106
+ (default || {}).merge (body || {})
107
+ end
108
+
109
+ end.tap { |mod| include(mod) }
110
+ end
111
+
112
+ end
113
+
110
114
  end
@@ -2,24 +2,24 @@ module Laundry
2
2
  module PaymentsGateway
3
3
 
4
4
  class AccountDriver < MerchantAuthenticatableDriver
5
-
5
+
6
6
  attr_accessor :client
7
7
  def initialize(client, merchant)
8
8
  super merchant
9
9
  self.client = client
10
10
  end
11
-
11
+
12
12
  def client_driver
13
13
  @client_driver ||= ClientDriver.new(self.merchant)
14
14
  end
15
-
15
+
16
16
  def find(payment_method_id)
17
17
  r = client_driver.get_payment_method({'ClientID' => self.client.id, 'PaymentMethodID' => payment_method_id}) do
18
18
  http.headers["SOAPAction"] = 'https://ws.paymentsgateway.net/v1/IClientService/getPaymentMethod'
19
19
  end
20
20
  Account.from_response(r, self.merchant)
21
21
  end
22
-
22
+
23
23
  # Returns the payment method id
24
24
  def create!(options = {})
25
25
  raise ArgumentError, "Tried to create an account on an invalid client." if self.client.nil? || self.client.blank?
@@ -30,7 +30,7 @@ module Laundry
30
30
  end
31
31
  r[:create_payment_method_response][:create_payment_method_result]
32
32
  end
33
-
33
+
34
34
  def self.prettifiable_fields
35
35
  ['MerchantID',
36
36
  'ClientID',
@@ -42,8 +42,8 @@ module Laundry
42
42
  'Note',
43
43
  'IsDefault']
44
44
  end
45
-
45
+
46
46
  end
47
-
47
+
48
48
  end
49
49
  end
@@ -2,7 +2,7 @@ module Laundry
2
2
  module PaymentsGateway
3
3
 
4
4
  class ClientDriver < MerchantAuthenticatableDriver
5
-
5
+
6
6
  # Setup WSDL
7
7
  def self.wsdl
8
8
  if Laundry.sandboxed?
@@ -11,16 +11,16 @@ module Laundry
11
11
  "https://ws.paymentsgateway.net/Service/v1/Client.wsdl"
12
12
  end
13
13
  end
14
-
14
+
15
15
  actions "createClient", "getClient", "getPaymentMethod", "createPaymentMethod"
16
-
16
+
17
17
  def find(id)
18
18
  r = get_client({'ClientID' => id}) do
19
19
  http.headers["SOAPAction"] = 'https://ws.paymentsgateway.net/v1/IClientService/getClient'
20
20
  end
21
21
  Client.from_response(r, self.merchant)
22
22
  end
23
-
23
+
24
24
  # Creates a client and returns the newly created client id.
25
25
  def create!(options = {})
26
26
  r = create_client({'client' => ClientDriver.default_hash.merge(options).merge({'MerchantID' => self.merchant.id, 'ClientID' => 0, 'Status' => 'Active'})} ) do
@@ -28,9 +28,9 @@ module Laundry
28
28
  end
29
29
  r[:create_client_response][:create_client_result]
30
30
  end
31
-
31
+
32
32
  private
33
-
33
+
34
34
  def self.default_fields
35
35
  ['MerchantID',
36
36
  'ClientID',
@@ -60,7 +60,7 @@ module Laundry
60
60
  'ConsumerID',
61
61
  'Status']
62
62
  end
63
-
63
+
64
64
  def self.default_hash
65
65
  h = {}
66
66
  self.default_fields.each do |f|
@@ -68,7 +68,7 @@ module Laundry
68
68
  end
69
69
  h
70
70
  end
71
-
71
+
72
72
  end
73
73
 
74
74
  end
@@ -3,24 +3,24 @@ module Laundry
3
3
 
4
4
  class MerchantAuthenticatableDriver
5
5
  extend Laundry::SOAPModel
6
-
6
+
7
7
  attr_accessor :merchant
8
-
8
+
9
9
  def initialize(merchant)
10
10
  # Save the merchant.
11
11
  self.merchant = merchant
12
12
  setup_client!
13
13
  end
14
-
14
+
15
15
  def setup_client!
16
16
  self.class.client.wsdl.document = self.class.wsdl if self.class.respond_to?(:wsdl)
17
17
  end
18
-
18
+
19
19
  def default_body
20
20
  # Log in via the merchant's login credentials.
21
21
  self.merchant.login_credentials.merge("MerchantID" => self.merchant.id)
22
22
  end
23
-
23
+
24
24
  def self.prettifiable_fields
25
25
  []
26
26
  end
@@ -40,8 +40,8 @@ module Laundry
40
40
  end
41
41
  ugly_hash
42
42
  end
43
-
43
+
44
44
  end
45
-
45
+
46
46
  end
47
47
  end
@@ -2,7 +2,7 @@ module Laundry
2
2
  module PaymentsGateway
3
3
 
4
4
  class SocketDriver < MerchantAuthenticatableDriver
5
-
5
+
6
6
  # Setup WSDL
7
7
  def self.wsdl
8
8
  if Laundry.sandboxed?
@@ -11,15 +11,15 @@ module Laundry
11
11
  "https://ws.paymentsgateway.net/pg/paymentsgateway.asmx?WSDL"
12
12
  end
13
13
  end
14
-
14
+
15
15
  actions "ExecuteSocketQuery"
16
-
16
+
17
17
  def exec(options = {})
18
18
  execute_socket_query(options) do
19
19
  http.headers["SOAPAction"] = "http://paymentsgateway.achdirect.com/ExecuteSocketQuery"
20
20
  end
21
21
  end
22
-
22
+
23
23
  end
24
24
 
25
25
  end
@@ -2,7 +2,7 @@ module Laundry
2
2
  module PaymentsGateway
3
3
 
4
4
  class TransactionDriver < MerchantAuthenticatableDriver
5
-
5
+
6
6
  # Setup WSDL
7
7
  def self.wsdl
8
8
  if Laundry.sandboxed?
@@ -11,9 +11,9 @@ module Laundry
11
11
  'https://ws.paymentsgateway.net/Service/v1/Transaction.wsdl'
12
12
  end
13
13
  end
14
-
14
+
15
15
  actions "getTransaction"
16
-
16
+
17
17
  def find(client_id, transaction_id)
18
18
  r = get_transaction({'ClientID' => client_id, 'TransactionID' => transaction_id}) do
19
19
  http.headers["SOAPAction"] = "https://ws.paymentsgateway.net/v1/ITransactionService/getTransaction"
@@ -2,7 +2,7 @@ module Laundry
2
2
  module PaymentsGateway
3
3
 
4
4
  class Account < ResponseModel
5
-
5
+
6
6
  def initialize_with_response(response)
7
7
  self.record = response[:get_payment_method_response][:get_payment_method_result][:payment_method]
8
8
  end
@@ -15,7 +15,7 @@ module Laundry
15
15
  EFT_VOID = 24
16
16
  EFT_FORCE = 25
17
17
  EFT_VERIFY_ONLY = 26
18
-
18
+
19
19
  def debit_cents(cents, *args)
20
20
  debit_dollars(dollarize(cents), *args)
21
21
  end
@@ -23,20 +23,20 @@ module Laundry
23
23
  def credit_cents(cents, *args)
24
24
  credit_dollars(dollarize(cents), *args)
25
25
  end
26
-
26
+
27
27
  def debit_dollars(dollars, *args)
28
28
  perform_transaction(dollars, EFT_SALE, *args)
29
29
  end
30
-
30
+
31
31
  def credit_dollars(dollars, *args)
32
32
  perform_transaction(dollars, EFT_CREDIT, *args)
33
33
  end
34
-
34
+
35
35
  def perform_transaction(dollars, type, options = {})
36
36
  require_merchant!
37
37
  options = {
38
- 'pg_merchant_id' => self.merchant.id,
39
- 'pg_password' => self.merchant.transaction_password,
38
+ 'pg_merchant_id' => self.merchant.id,
39
+ 'pg_password' => self.merchant.transaction_password,
40
40
  'pg_total_amount' => dollars,
41
41
  'pg_client_id' => self.client_id,
42
42
  'pg_payment_method_id' => self.id,
@@ -45,13 +45,13 @@ module Laundry
45
45
  r = self.merchant.socket_driver.exec(options)
46
46
  TransactionResponse.from_response(r, self.merchant)
47
47
  end
48
-
48
+
49
49
  def id
50
50
  payment_method_id
51
51
  end
52
-
52
+
53
53
  private
54
-
54
+
55
55
  def dollarize(cents)
56
56
  cents.to_f / 100.0
57
57
  end
@@ -2,7 +2,7 @@ module Laundry
2
2
  module PaymentsGateway
3
3
 
4
4
  class Client < ResponseModel
5
-
5
+
6
6
  def initialize_with_response(response)
7
7
  self.record = response[:get_client_response][:get_client_result][:client_record]
8
8
  end
@@ -10,13 +10,13 @@ module Laundry
10
10
  def id
11
11
  client_id
12
12
  end
13
-
13
+
14
14
  def accounts_driver
15
15
  require_merchant!
16
16
  AccountDriver.new(self, self.merchant)
17
17
  end
18
18
  alias_method :accounts, :accounts_driver
19
-
19
+
20
20
  end
21
21
 
22
22
  end
@@ -3,51 +3,51 @@ module Laundry
3
3
 
4
4
  class Merchant
5
5
  extend Laundry::SOAPModel
6
-
6
+
7
7
  # Setup WSDL
8
8
  if Laundry.sandboxed?
9
9
  document "https://sandbox.paymentsgateway.net/WS/Merchant.wsdl"
10
10
  else
11
11
  document "https://ws.paymentsgateway.net/Service/v1/Merchant.wsdl"
12
12
  end
13
-
13
+
14
14
  def self.default_merchant(options = nil)
15
15
  @@default_merchant = Merchant.new(options) if options
16
16
  @@default_merchant
17
17
  end
18
-
18
+
19
19
  attr_accessor :id, :api_login_id, :api_password, :transaction_password
20
-
20
+
21
21
  def initialize(options = {})
22
-
22
+
23
23
  self.id = options[:id]
24
24
  self.api_login_id = options[:api_login_id]
25
25
  self.api_password = options[:api_password]
26
26
  self.transaction_password = options[:transaction_password]
27
-
27
+
28
28
  end
29
-
29
+
30
30
  def client_driver
31
31
  @client_driver ||= ClientDriver.new(self)
32
32
  end
33
33
  alias_method :clients, :client_driver
34
-
34
+
35
35
  def transaction_driver
36
36
  @transaction_driver ||= TransactionDriver.new(self)
37
37
  end
38
38
  alias_method :transactions, :transaction_driver
39
-
39
+
40
40
  def socket_driver
41
41
  @socket_driver ||= SocketDriver.new(self)
42
42
  end
43
-
43
+
44
44
  def login_credentials
45
45
  # Time diff from 1/1/0001 00:00:00 to 1/1/1970 00:00:00
46
46
  utc_time = (Time.now.to_i + 62135596800).to_s + '0000000'
47
47
  ts_hash = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new('md5'), self.api_password, "#{self.api_login_id}|#{utc_time}")
48
48
  {'ticket' => {'APILoginID' => self.api_login_id, 'TSHash' => ts_hash, 'UTCTime' => utc_time}}
49
49
  end
50
-
50
+
51
51
  end
52
52
 
53
53
  end
@@ -7,40 +7,40 @@ module Laundry
7
7
 
8
8
  attr_accessor :record
9
9
  attr_accessor :merchant
10
-
10
+
11
11
  def self.from_response(response, merchant)
12
12
  model = self.new
13
13
  model.merchant = merchant
14
14
  model.initialize_with_response response
15
15
  model
16
16
  end
17
-
17
+
18
18
  def initialize_with_response(response)
19
19
  self.record = response.try(:to_hash) || {}
20
20
  end
21
-
21
+
22
22
  def to_hash
23
23
  record.try(:to_hash)
24
24
  end
25
-
25
+
26
26
  def blank?
27
27
  record == {} || record.nil? || !record
28
28
  end
29
-
29
+
30
30
  def require_merchant!
31
31
  unless merchant && merchant.class == Laundry::PaymentsGateway::Merchant
32
32
  raise MerchantNotSetError, "Tried to call a method that requires a merchant to be set on the model. Try calling merchant= first."
33
33
  end
34
34
  end
35
-
35
+
36
36
  def method_missing(id, *args)
37
37
  return record[id.to_sym] if record.is_a?(Hash) && record.has_key?(id.to_sym)
38
38
  super
39
39
  end
40
-
40
+
41
41
  ## Support cleaner serialization to ActiveRecord
42
42
  ## Apparently supported in Rails >= 3.1
43
-
43
+
44
44
  # We don't want to serialize the merchant because it has upsetting
45
45
  # amounts of passwords in it.
46
46
  def dumpable
@@ -48,11 +48,11 @@ module Laundry
48
48
  d.merchant = nil
49
49
  d
50
50
  end
51
-
51
+
52
52
  def self.dump(model)
53
53
  model ? YAML::dump(model.dumpable) : nil
54
54
  end
55
-
55
+
56
56
  def self.load(model_text)
57
57
  model_text ? YAML::load(model_text) : nil
58
58
  end
@@ -2,15 +2,15 @@ module Laundry
2
2
  module PaymentsGateway
3
3
 
4
4
  class Transaction < ResponseModel
5
-
5
+
6
6
  def initialize_with_response(response)
7
7
  self.record = response[:get_transaction_response][:get_transaction_result]
8
8
  end
9
-
9
+
10
10
  def status
11
11
  record[:response][:status].downcase
12
12
  end
13
-
13
+
14
14
  end
15
15
 
16
16
  end
@@ -1,36 +1,37 @@
1
1
  module Laundry
2
- module PaymentsGateway
3
-
4
- class TransactionResponse < ResponseModel
5
-
6
- def initialize_with_response(response)
7
- self.record = parse(response)
8
- end
9
-
10
- def success?
11
- pg_response_type == 'A'
12
- end
13
-
14
- def full_transaction
15
- require_merchant!
16
- self.merchant.transactions.find pg_payment_method_id, pg_trace_number
17
- end
18
-
19
- private
20
-
21
- def parse(response)
22
- data = {}
23
- res = response[:execute_socket_query_response][:execute_socket_query_result].split("\n")
24
- res.each do |key_value_pair|
25
- kv = key_value_pair.split('=')
26
- if kv.size == 2
27
- data[ kv[0].to_sym ] = kv[1]
28
- end
29
- end
30
- data
31
- end
32
-
33
- end
34
-
35
- end
2
+ module PaymentsGateway
3
+
4
+ class TransactionResponse < ResponseModel
5
+
6
+ def initialize_with_response(response)
7
+ self.record = parse(response)
8
+ end
9
+
10
+ def success?
11
+ pg_response_type == 'A'
12
+ end
13
+
14
+ def full_transaction
15
+ require_merchant!
16
+ self.merchant.transactions.find pg_payment_method_id, pg_trace_number
17
+ end
18
+
19
+ private
20
+
21
+ def parse(response)
22
+ return response if response.is_a? Hash
23
+ data = {}
24
+ res = response[:execute_socket_query_response][:execute_socket_query_result].split("\n")
25
+ res.each do |key_value_pair|
26
+ kv = key_value_pair.split('=')
27
+ if kv.size == 2
28
+ data[ kv[0].to_sym ] = kv[1]
29
+ end
30
+ end
31
+ data
32
+ end
33
+
34
+ end
35
+
36
+ end
36
37
  end
@@ -0,0 +1,44 @@
1
+ require "rspec/mocks/standalone"
2
+ require 'factory_girl'
3
+ factories_dir = File.expand_path File.join(__FILE__, "..", "..", "..", "spec", "factories")
4
+ puts factories_dir
5
+ FactoryGirl.definition_file_paths = [factories_dir]
6
+ FactoryGirl.find_definitions
7
+ include FactoryGirl::Syntax::Methods
8
+
9
+ class Module
10
+ def subclasses
11
+ classes = []
12
+ ObjectSpace.each_object(Module) do |m|
13
+ classes << m if m.ancestors.include? self
14
+ end
15
+ classes
16
+ end
17
+ end
18
+
19
+ def stub_all
20
+ # Just stub away all the SOAP requests and such.
21
+ classes = [Laundry::PaymentsGateway::MerchantAuthenticatableDriver, Laundry::PaymentsGateway::Merchant]
22
+ classes.map{|c| [c.subclasses, c] }.flatten.uniq.each do |klass|
23
+ klass.stub(:client_request).and_return true
24
+ klass.stub(:client).and_return true
25
+ klass.any_instance.stub(:setup_client!).and_return true
26
+ end
27
+
28
+ # Stub client driver
29
+ Laundry::PaymentsGateway::ClientDriver.any_instance.stub(:find).and_return(build(:client))
30
+ Laundry::PaymentsGateway::ClientDriver.any_instance.stub(:create!).and_return(build(:client).id)
31
+
32
+
33
+ # Stub account driver
34
+ Laundry::PaymentsGateway::AccountDriver.any_instance.stub(:find).and_return(build(:account))
35
+ Laundry::PaymentsGateway::AccountDriver.any_instance.stub(:create!).and_return(build(:account).id)
36
+
37
+ # Stub performing transactions.
38
+ Laundry::PaymentsGateway::Account.any_instance.stub(:perform_transaction).and_return(build(:transaction_response))
39
+
40
+ Laundry.stub(:stubbed?).and_return true
41
+ end
42
+
43
+ # Run this code once on the first require to ensure all methods are stubbed
44
+ stub_all
@@ -1,3 +1,3 @@
1
1
  module Laundry
2
- VERSION = "0.0.5"
2
+ VERSION = "0.0.6"
3
3
  end
@@ -0,0 +1,10 @@
1
+ # Read about factories at http://github.com/thoughtbot/factory_girl
2
+
3
+ FactoryGirl.define do
4
+ factory :account, class: Laundry::PaymentsGateway::Client do
5
+ payment_method_id '1'
6
+ initialize_with do
7
+ Laundry::PaymentsGateway::Account.from_response({get_payment_method_response: {get_payment_method_result: {payment_method: attributes}}}, FactoryGirl.build(:merchant))
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ # Read about factories at http://github.com/thoughtbot/factory_girl
2
+
3
+ FactoryGirl.define do
4
+ factory :client, class: Laundry::PaymentsGateway::Client do
5
+ client_id '1'
6
+ initialize_with do
7
+ Laundry::PaymentsGateway::Client.from_response({get_client_response: {get_client_result: {client_record: attributes}}}, FactoryGirl.build(:merchant))
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ # Read about factories at http://github.com/thoughtbot/factory_girl
2
+
3
+ FactoryGirl.define do
4
+ factory :merchant, class: Laundry::PaymentsGateway::Merchant do
5
+ id '123456'
6
+ api_login_id 'abc123'
7
+ api_password 'secretsauce'
8
+ transaction_password 'moneymoneymoney'
9
+ end
10
+ end
@@ -0,0 +1,8 @@
1
+ # Read about factories at http://github.com/thoughtbot/factory_girl
2
+
3
+ FactoryGirl.define do
4
+ factory :transaction_response, class: Laundry::PaymentsGateway::TransactionResponse do
5
+ pg_response_type "A"
6
+ initialize_with { Laundry::PaymentsGateway::TransactionResponse.from_response(attributes, FactoryGirl.build(:merchant)) }
7
+ end
8
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe Laundry::PaymentsGateway::Merchant do
4
+
5
+ let(:merchant) { build :merchant }
6
+
7
+ describe "#find" do
8
+ before do
9
+ Laundry.stub!
10
+ end
11
+
12
+ it "returns a client" do
13
+ merchant.clients.find(10).class.should == Laundry::PaymentsGateway::Client
14
+ end
15
+
16
+ it "client should have an id" do
17
+ merchant.clients.find(10).id.should == "1"
18
+ end
19
+ end
20
+
21
+ end
@@ -0,0 +1,17 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper.rb"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+ require 'factory_girl'
8
+
9
+ RSpec.configure do |config|
10
+ config.treat_symbols_as_metadata_keys_with_true_values = true
11
+ config.run_all_when_everything_filtered = true
12
+ config.filter_run :focus
13
+ config.include FactoryGirl::Syntax::Methods
14
+ end
15
+
16
+ require_relative "../lib/laundry"
17
+ Laundry.stub!
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: laundry
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-08-10 00:00:00.000000000 Z
12
+ date: 2012-08-31 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: savon
@@ -27,6 +27,54 @@ dependencies:
27
27
  - - ! '>='
28
28
  - !ruby/object:Gem::Version
29
29
  version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: factory_girl
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: debugger
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
30
78
  description: A soapy interface to ACH Direct's PaymentsGateway.com service.
31
79
  email:
32
80
  - supapuerco@gmail.com
@@ -35,12 +83,14 @@ extensions: []
35
83
  extra_rdoc_files: []
36
84
  files:
37
85
  - .gitignore
86
+ - .rspec
38
87
  - CHANGELOG.md
39
88
  - Gemfile
40
89
  - LICENSE
41
90
  - README.md
42
91
  - Rakefile
43
92
  - laundry.gemspec
93
+ - laundry.sublime-project
44
94
  - lib/laundry.rb
45
95
  - lib/laundry/lib/soap_model.rb
46
96
  - lib/laundry/payments_gateway/drivers/account_driver.rb
@@ -55,7 +105,14 @@ files:
55
105
  - lib/laundry/payments_gateway/models/transaction.rb
56
106
  - lib/laundry/payments_gateway/models/transaction_response.rb
57
107
  - lib/laundry/payments_gateway/payments_gateway.rb
108
+ - lib/laundry/stubbed.rb
58
109
  - lib/laundry/version.rb
110
+ - spec/factories/accounts_factory.rb
111
+ - spec/factories/clients_factory.rb
112
+ - spec/factories/merchants_factory.rb
113
+ - spec/factories/transaction_response_factory.rb
114
+ - spec/laundry/merchant_spec.rb
115
+ - spec/spec_helper.rb
59
116
  homepage: https://github.com/supapuerco/laundry
60
117
  licenses: []
61
118
  post_install_message:
@@ -70,7 +127,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
70
127
  version: '0'
71
128
  segments:
72
129
  - 0
73
- hash: -3800532644211602435
130
+ hash: -4411116969965596496
74
131
  required_rubygems_version: !ruby/object:Gem::Requirement
75
132
  none: false
76
133
  requirements:
@@ -79,11 +136,17 @@ required_rubygems_version: !ruby/object:Gem::Requirement
79
136
  version: '0'
80
137
  segments:
81
138
  - 0
82
- hash: -3800532644211602435
139
+ hash: -4411116969965596496
83
140
  requirements: []
84
141
  rubyforge_project:
85
142
  rubygems_version: 1.8.24
86
143
  signing_key:
87
144
  specification_version: 3
88
145
  summary: A soapy interface to ACH Direct's PaymentsGateway.com service.
89
- test_files: []
146
+ test_files:
147
+ - spec/factories/accounts_factory.rb
148
+ - spec/factories/clients_factory.rb
149
+ - spec/factories/merchants_factory.rb
150
+ - spec/factories/transaction_response_factory.rb
151
+ - spec/laundry/merchant_spec.rb
152
+ - spec/spec_helper.rb