laundry 0.0.5 → 0.0.6

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