arrow_payments 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/.gitignore +25 -0
  2. data/.rspec +2 -0
  3. data/.travis.yml +3 -0
  4. data/Gemfile +3 -0
  5. data/README.md +272 -0
  6. data/Rakefile +10 -0
  7. data/arrow_payments.gemspec +25 -0
  8. data/lib/arrow_payments.rb +25 -0
  9. data/lib/arrow_payments/address.rb +11 -0
  10. data/lib/arrow_payments/client.rb +57 -0
  11. data/lib/arrow_payments/client/customers.rb +45 -0
  12. data/lib/arrow_payments/client/payment_methods.rb +90 -0
  13. data/lib/arrow_payments/client/transactions.rb +64 -0
  14. data/lib/arrow_payments/configuration.rb +7 -0
  15. data/lib/arrow_payments/connection.rb +78 -0
  16. data/lib/arrow_payments/customer.rb +38 -0
  17. data/lib/arrow_payments/entity.rb +39 -0
  18. data/lib/arrow_payments/errors.rb +13 -0
  19. data/lib/arrow_payments/line_item.rb +10 -0
  20. data/lib/arrow_payments/payment_method.rb +19 -0
  21. data/lib/arrow_payments/recurring_billing.rb +30 -0
  22. data/lib/arrow_payments/transaction.rb +74 -0
  23. data/lib/arrow_payments/version.rb +3 -0
  24. data/spec/arrow_payments_spec.rb +40 -0
  25. data/spec/client_spec.rb +42 -0
  26. data/spec/configuration_spec.rb +24 -0
  27. data/spec/customer_spec.rb +29 -0
  28. data/spec/customers_spec.rb +160 -0
  29. data/spec/entity_spec.rb +55 -0
  30. data/spec/fixtures/complete_payment_method.json +37 -0
  31. data/spec/fixtures/customer.json +25 -0
  32. data/spec/fixtures/customers.json +133 -0
  33. data/spec/fixtures/headers/payment_method_complete.yml +9 -0
  34. data/spec/fixtures/headers/payment_method_start.yml +9 -0
  35. data/spec/fixtures/line_item.json +8 -0
  36. data/spec/fixtures/start_payment_method.json +6 -0
  37. data/spec/fixtures/transaction.json +29 -0
  38. data/spec/fixtures/transaction_capture.json +5 -0
  39. data/spec/fixtures/transactions.json +68 -0
  40. data/spec/line_item_spec.rb +17 -0
  41. data/spec/payment_method_spec.rb +16 -0
  42. data/spec/payment_methods_spec.rb +181 -0
  43. data/spec/recurring_billing_spec.rb +28 -0
  44. data/spec/spec_helper.rb +19 -0
  45. data/spec/transactions_spec.rb +101 -0
  46. metadata +225 -0
@@ -0,0 +1,74 @@
1
+ require 'time'
2
+
3
+ module ArrowPayments
4
+ class Transaction < Entity
5
+ # Indicates status of transaction
6
+ STATUSES = [
7
+ 'NotSettled',
8
+ 'Settled',
9
+ 'Voided',
10
+ 'Failed'
11
+ ]
12
+
13
+ # Indicates how the transation was entered
14
+ SOURCES = [
15
+ 'VirtualTerminal',
16
+ 'LinkedTerminal',
17
+ 'API',
18
+ 'Subscription',
19
+ 'InvoicePayment',
20
+ 'Mobile'
21
+ ]
22
+
23
+ property :id, :from => 'ID'
24
+ property :account, :from => 'Account'
25
+ property :transaction_type, :from => 'TransactionType'
26
+ property :created_at, :from => 'TransactionTime'
27
+ property :level, :from => 'Level'
28
+ property :total_amount, :from => 'TotalAmount'
29
+ property :description, :from => 'Description'
30
+ property :transaction_source, :from => 'TransactionSource'
31
+ property :status, :from => 'Status'
32
+ property :capture_amount, :from => 'CaptureAmount'
33
+ property :authorization_code, :from => 'AuthorizationCode'
34
+ property :payment_method_id, :from => 'PaymentMethodID'
35
+ property :cardholder_first_name, :from => 'CardholderFirstName'
36
+ property :cardholder_last_name, :from => 'CardholderLastName'
37
+ property :card_type, :from => 'CardType'
38
+ property :card_last_digits, :from => 'CardLast4'
39
+ property :customer_po_number, :from => 'CustomerPONumber'
40
+ property :tax_amount, :from => 'TaxAmount'
41
+ property :shipping_amount, :from => 'ShippingAmount'
42
+ property :shipping_address_id, :from => 'ShippingAddressID'
43
+ property :shipping_address, :from => 'Shipping'
44
+ property :customer_id, :from => 'CustomerID'
45
+ property :customer_name, :from => 'CustomerName'
46
+ property :line_items, :from => 'LineItems'
47
+ property :billing_address, :from => 'Billing'
48
+
49
+ def Billing=(data)
50
+ if data.kind_of?(Hash)
51
+ self.billing_address = ArrowPayments::Address.new(data)
52
+ end
53
+ end
54
+
55
+ def Shipping=(data)
56
+ if data.kind_of?(Hash)
57
+ self.shipping_address = ArrowPayments::Address.new(data)
58
+ end
59
+ end
60
+
61
+ def LineItems=(data)
62
+ if data.kind_of?(Array)
63
+ self.line_items = data.map { |hash| ArrowPayments::LineItem.new(hash) }
64
+ end
65
+ end
66
+
67
+ def TransactionTime=(data)
68
+ if data =~ /^\/Date\(([\d]+)\)\/$/
69
+ epoch = Integer($1[0..9])
70
+ self.created_at = Time.at(epoch)
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,3 @@
1
+ module ArrowPayments
2
+ VERSION = '0.1.1'
3
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+
3
+ describe ArrowPayments do
4
+ describe '#client' do
5
+ it 'returns client instance for options' do
6
+ client = ArrowPayments.client(
7
+ :api_key => 'foo',
8
+ :mode => 'production',
9
+ :merchant_id => 12345
10
+ )
11
+
12
+ client.should be_a ArrowPayments::Client
13
+ client.api_key.should eq('foo')
14
+ client.mode.should eq('production')
15
+ client.merchant_id.should eq(12345)
16
+ client.debug.should eq(false)
17
+ end
18
+
19
+ context 'when preconfigured' do
20
+ before do
21
+ ArrowPayments::Configuration.api_key = 'bar'
22
+ ArrowPayments::Configuration.mode = 'sandbox'
23
+ ArrowPayments::Configuration.merchant_id = 12345
24
+ end
25
+
26
+ after do
27
+ ArrowPayments::Configuration.api_key = nil
28
+ ArrowPayments::Configuration.mode = nil
29
+ ArrowPayments::Configuration.merchant_id = nil
30
+ end
31
+
32
+ it 'returns preconfigured client instance' do
33
+ client = ArrowPayments.client
34
+ client.api_key.should eq('bar')
35
+ client.mode.should eq('sandbox')
36
+ client.merchant_id.should eq(12345)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ describe ArrowPayments::Client do
4
+ describe '#initialize' do
5
+ it 'requires an api key' do
6
+ expect { ArrowPayments::Client.new }.to raise_error "API key required"
7
+ end
8
+
9
+ it 'requires a merchant id' do
10
+ expect { ArrowPayments::Client.new(:api_key => 'foobar') }.
11
+ to raise_error "Merchant ID required"
12
+ end
13
+
14
+ it 'validates api mode' do
15
+ expect { ArrowPayments::Client.new(:api_key => 'foobar', :merchant_id => 'foobar', :mode => 'foobar') }.
16
+ to raise_error "Invalid mode: foobar"
17
+ end
18
+
19
+ it 'sets production mode by default' do
20
+ client = ArrowPayments::Client.new(:api_key => 'foobar', :merchant_id => 'foobar')
21
+ client.mode.should eq('production')
22
+ end
23
+ end
24
+
25
+ describe '#sanbox?' do
26
+ it 'returns true if sandbox mode' do
27
+ client = ArrowPayments::Client.new(:api_key => 'foobar', :merchant_id => 'foobar', :mode => 'sandbox')
28
+
29
+ client.sandbox?.should be_true
30
+ client.production?.should_not be_true
31
+ end
32
+ end
33
+
34
+ describe '#production?' do
35
+ it 'returns true if production mode' do
36
+ client = ArrowPayments::Client.new(:api_key => 'foobar', :merchant_id => 'foobar', :mode => 'production')
37
+
38
+ client.sandbox?.should be_false
39
+ client.production?.should be_true
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe ArrowPayments::Configuration do
4
+ describe '#api_key=' do
5
+ it 'sets gateway api key' do
6
+ ArrowPayments::Configuration.api_key = 'foobar'
7
+ ArrowPayments::Configuration.api_key.should eq('foobar')
8
+ end
9
+ end
10
+
11
+ describe '#mode=' do
12
+ it 'sets gateway mode' do
13
+ ArrowPayments::Configuration.mode = 'production'
14
+ ArrowPayments::Configuration.mode.should eq('production')
15
+ end
16
+ end
17
+
18
+ describe '#merchant_id=' do
19
+ it 'sets merchant ID' do
20
+ ArrowPayments::Configuration.merchant_id = 12345
21
+ ArrowPayments::Configuration.merchant_id.should eq(12345)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe ArrowPayments::Customer do
4
+ it { should respond_to :id }
5
+ it { should respond_to :name }
6
+ it { should respond_to :code }
7
+ it { should respond_to :contact }
8
+ it { should respond_to :phone }
9
+ it { should respond_to :email }
10
+ it { should respond_to :recurring_billings }
11
+ it { should respond_to :payment_methods }
12
+
13
+ describe '#new' do
14
+ let(:customer_data) { json_fixture('customer.json') }
15
+
16
+ it 'properly assigns attributes' do
17
+ customer = ArrowPayments::Customer.new(customer_data)
18
+
19
+ customer.id.should eq(10162)
20
+ customer.name.should eq('First Supplies')
21
+ customer.code.should eq('First Supplies')
22
+ customer.contact.should eq('John Peoples')
23
+ customer.phone.should eq('8325539616')
24
+ customer.email.should eq('John.Peoples@arrow-test.com')
25
+ customer.recurring_billings.should be_empty
26
+ customer.payment_methods.should_not be_empty
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,160 @@
1
+ require 'spec_helper'
2
+
3
+ describe ArrowPayments::Customers do
4
+ let(:client) { ArrowPayments.client }
5
+
6
+ before :all do
7
+ ArrowPayments::Configuration.api_key = 'foobar'
8
+ ArrowPayments::Configuration.merchant_id = 'foo'
9
+ ArrowPayments::Configuration.mode = 'sandbox'
10
+ end
11
+
12
+ describe '#customers' do
13
+ before do
14
+ stub_request(:get, "http://demo.arrowpayments.com/api/foobar/customers").
15
+ to_return(:status => 200, :body => fixture('customers.json'))
16
+ end
17
+
18
+ it 'returns an array of existing customers' do
19
+ customers = client.customers
20
+ customers.should be_an Array
21
+ customers.size.should eq(12)
22
+ end
23
+ end
24
+
25
+ describe '#customer' do
26
+ it 'returns an existing customer by ID' do
27
+ stub_request(:get, "http://demo.arrowpayments.com/api/foobar/customer/10162").
28
+ to_return(:status => 200, :body => fixture('customer.json'))
29
+
30
+ customer = client.customer(10162)
31
+ customer.should_not be_nil
32
+ customer.id.should eq(10162)
33
+ customer.name.should eq('First Supplies')
34
+ end
35
+
36
+ it 'returns nil if customer does not exist' do
37
+ stub_request(:get, "http://demo.arrowpayments.com/api/foobar/customer/12345").
38
+ to_return(:status => 404, :body => "", :headers => {:error => "Customer Not Found"})
39
+
40
+ customer = client.customer(12345)
41
+ customer.should be_nil
42
+ end
43
+ end
44
+
45
+ describe '#create_customer' do
46
+ let(:customer) do
47
+ ArrowPayments::Customer.new(
48
+ :name => 'First Supplies',
49
+ :code => 'First Supplies',
50
+ :contact => 'John Peoples',
51
+ :email => 'John.Peoples@arrow-test.com',
52
+ :phone => '8325539616'
53
+ )
54
+ end
55
+
56
+ it 'creates and returns a new customer' do
57
+ stub_request(:post, "http://demo.arrowpayments.com/api/customer/add").
58
+ with(
59
+ :body => "{\"Name\":\"First Supplies\",\"Code\":\"First Supplies\",\"PrimaryContact\":\"John Peoples\",\"PrimaryContactPhone\":\"8325539616\",\"PrimaryContactEmailAddress\":\"John.Peoples@arrow-test.com\",\"PaymentMethods\":[],\"ApiKey\":\"foobar\",\"MID\":\"foo\"}",
60
+ :headers => {'Accept'=>'application/json', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby'}
61
+ ).
62
+ to_return(:status => 200, :body => fixture('customer.json'), :headers => {})
63
+
64
+ new_customer = client.create_customer(customer)
65
+ new_customer.id.should eq(10162)
66
+ end
67
+
68
+ it 'raises error when unable to create' do
69
+ stub_request(:post, "http://demo.arrowpayments.com/api/customer/add").
70
+ with(
71
+ :body => "{\"Name\":\"First Supplies\",\"Code\":\"First Supplies\",\"PrimaryContact\":\"John Peoples\",\"PrimaryContactPhone\":\"8325539616\",\"PrimaryContactEmailAddress\":\"John.Peoples@arrow-test.com\",\"PaymentMethods\":[],\"ApiKey\":\"foobar\",\"MID\":\"foo\"}",
72
+ :headers => {'Accept'=>'application/json', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby'}
73
+ ).
74
+ to_return(:status => 500, :body => "", :headers => {:error => "Customer with Name First Supplies already exists for merchant"})
75
+
76
+ expect { client.create_customer(customer) }.to raise_error ArrowPayments::Error, 'Customer with Name First Supplies already exists for merchant'
77
+ end
78
+ end
79
+
80
+ describe '#update_customer' do
81
+ it 'raises error if customer does not exist' do
82
+ stub_request(:get, "http://demo.arrowpayments.com/api/foobar/customer/10162").
83
+ to_return(:status => 200, :body => fixture('customer.json'))
84
+
85
+ stub_request(:post, "http://demo.arrowpayments.com/api/customer/update").
86
+ with(
87
+ :body => "{\"ID\":\"10163\",\"Name\":\"Foobar\",\"Code\":\"First Supplies\",\"PrimaryContact\":\"John Peoples\",\"PrimaryContactPhone\":\"8325539616\",\"PrimaryContactEmailAddress\":\"John.Peoples@arrow-test.com\",\"RecurrentBilling\":[],\"PaymentMethods\":[{\"ID\":12436,\"CardType\":\"Visa\",\"Last4\":\"1111\",\"CardholderFirstName\":\"Paola\",\"CardholderLastName\":\"Chen\",\"ExpirationMonth\":6,\"ExpirationYear\":2015,\"BillingStreet1\":\"7495 Center St.\",\"BillingCity\":\"Chicago\",\"BillingState\":\"IL\",\"BillingZip\":\"60601\"}],\"CustomerID\":\"10163\",\"ApiKey\":\"foobar\",\"MID\":\"foo\"}",
88
+ :headers => {'Accept'=>'application/json', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby'}
89
+ ).
90
+ to_return(:status => 404, :body => "", :headers => {:error => "Customer Not Found"})
91
+
92
+ customer = client.customer('10162')
93
+ customer.id = '10163'
94
+ customer.name = 'Foobar'
95
+
96
+ expect { client.update_customer(customer) }.
97
+ to raise_error ArrowPayments::NotFound, 'Customer Not Found'
98
+ end
99
+
100
+ it 'raises error if customer is not valid' do
101
+ stub_request(:get, "http://demo.arrowpayments.com/api/foobar/customer/10162").
102
+ to_return(:status => 200, :body => fixture('customer.json'))
103
+
104
+ stub_request(:post, "http://demo.arrowpayments.com/api/customer/update").
105
+ with(
106
+ :body => "{\"ID\":10162,\"Name\":\"Foobar\",\"Code\":\"First Supplies\",\"PrimaryContact\":\"John Peoples\",\"PrimaryContactPhone\":\"8325539616\",\"PrimaryContactEmailAddress\":\"John.Peoples@arrow-test.com\",\"RecurrentBilling\":[],\"PaymentMethods\":[{\"ID\":12436,\"CardType\":\"Visa\",\"Last4\":\"1111\",\"CardholderFirstName\":\"Paola\",\"CardholderLastName\":\"Chen\",\"ExpirationMonth\":6,\"ExpirationYear\":2015,\"BillingStreet1\":\"7495 Center St.\",\"BillingCity\":\"Chicago\",\"BillingState\":\"IL\",\"BillingZip\":\"60601\"}],\"CustomerID\":10162,\"ApiKey\":\"foobar\",\"MID\":\"foo\"}",
107
+ :headers => {'Accept'=>'application/json', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby'}
108
+ ).
109
+ to_return(:status => 500, :body => "", :headers => {:error => "Customer with Name Foobar already exists for merchant"})
110
+
111
+ customer = client.customer('10162')
112
+ customer.name = 'Foobar'
113
+
114
+ expect { client.update_customer(customer) }.
115
+ to raise_error ArrowPayments::Error, 'Customer with Name Foobar already exists for merchant'
116
+ end
117
+
118
+ it 'returns true if customer was updated' do
119
+ stub_request(:get, "http://demo.arrowpayments.com/api/foobar/customer/10162").
120
+ to_return(:status => 200, :body => fixture('customer.json'))
121
+
122
+ stub_request(:post, "http://demo.arrowpayments.com/api/customer/update").
123
+ with(
124
+ :body => "{\"ID\":10162,\"Name\":\"Foobar\",\"Code\":\"First Supplies\",\"PrimaryContact\":\"John Peoples\",\"PrimaryContactPhone\":\"8325539616\",\"PrimaryContactEmailAddress\":\"John.Peoples@arrow-test.com\",\"RecurrentBilling\":[],\"PaymentMethods\":[{\"ID\":12436,\"CardType\":\"Visa\",\"Last4\":\"1111\",\"CardholderFirstName\":\"Paola\",\"CardholderLastName\":\"Chen\",\"ExpirationMonth\":6,\"ExpirationYear\":2015,\"BillingStreet1\":\"7495 Center St.\",\"BillingCity\":\"Chicago\",\"BillingState\":\"IL\",\"BillingZip\":\"60601\"}],\"CustomerID\":10162,\"ApiKey\":\"foobar\",\"MID\":\"foo\"}",
125
+ :headers => {'Accept'=>'application/json', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby'}
126
+ ).
127
+ to_return(:status => 200, :body => {'Success' => true}.to_json)
128
+
129
+ customer = client.customer('10162')
130
+ customer.name = 'Foobar'
131
+
132
+ client.update_customer(customer).should be_true
133
+ end
134
+ end
135
+
136
+ describe '#delete_customer' do
137
+ it 'raises error if customer does not exist' do
138
+ stub_request(:post, "http://demo.arrowpayments.com/api/customer/delete").
139
+ with(
140
+ :body => "{\"CustomerID\":10162,\"ApiKey\":\"foobar\",\"MID\":\"foo\"}",
141
+ :headers => {'Accept'=>'application/json', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby'}
142
+ ).
143
+ to_return(:status => 404, :body => "", :headers => {:error => "Customer Not Found"})
144
+
145
+ expect { client.delete_customer(10162) }.
146
+ to raise_error ArrowPayments::NotFound, 'Customer Not Found'
147
+ end
148
+
149
+ it 'returns true if customer was deleted' do
150
+ stub_request(:post, "http://demo.arrowpayments.com/api/customer/delete").
151
+ with(
152
+ :body => "{\"CustomerID\":10162,\"ApiKey\":\"foobar\",\"MID\":\"foo\"}",
153
+ :headers => {'Accept'=>'application/json', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby'}
154
+ ).
155
+ to_return(:status => 200, :body => {'Success' => true}.to_json)
156
+
157
+ client.delete_customer(10162).should be_true
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+
3
+ class EntityTester < ArrowPayments::Entity
4
+ property :foo, :from => 'Foo'
5
+ property :foobar, :from => 'Foobar'
6
+ property :foo_bar, :from => 'FooBar'
7
+ end
8
+
9
+ describe ArrowPayments::Entity do
10
+ let(:attributes) do
11
+ {'Foo' => 'a', 'Foobar' => 'b', 'FooBar' => 'c'}
12
+ end
13
+
14
+ describe '#new' do
15
+ it 'assigns properties' do
16
+ entity = EntityTester.new(attributes)
17
+
18
+ entity.foo.should eq('a')
19
+ entity.foobar.should eq('b')
20
+ entity.foo_bar.should eq('c')
21
+ end
22
+
23
+ it 'ignores undefined attributes' do
24
+ entity = EntityTester.new(attributes.merge('Bro' => 'Sup?'))
25
+
26
+ expect { entity.foo }.not_to raise_error
27
+ expect { entity['Bro'] }.to raise_error NoMethodError
28
+ end
29
+ end
30
+
31
+ describe '#properties_map' do
32
+ it 'returns hash with property mappings' do
33
+ map = EntityTester.properties_map
34
+
35
+ map.should be_a Hash
36
+ map.should_not be_empty
37
+ map.keys.should include(:foo, :foobar, :foo_bar)
38
+ map[:foo].should eq('Foo')
39
+ map[:foobar].should eq('Foobar')
40
+ map[:foo_bar].should eq('FooBar')
41
+ end
42
+ end
43
+
44
+ describe '#to_source_hash' do
45
+ it 'returns hash as source format' do
46
+ entity = EntityTester.new(attributes)
47
+ hash = entity.to_source_hash
48
+
49
+ hash.size.should eq(3)
50
+ hash['Foo'].should eq('a')
51
+ hash['Foobar'].should eq('b')
52
+ hash['FooBar'].should eq('c')
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,37 @@
1
+ {
2
+ "Account": null,
3
+ "TransactionType": null,
4
+ "TransactionTime": null,
5
+ "Level": 0,
6
+ "TotalAmount": 0,
7
+ "Description": null,
8
+ "TransactionSource": null,
9
+ "Status": null,
10
+ "CaptureAmount": 0,
11
+ "AuthorizationCode": null,
12
+ "PaymentMethodID": 14240,
13
+ "CardholderFirstName": "John",
14
+ "CardholderLastName": "Doe",
15
+ "CardType": null,
16
+ "CardLast4": "1111",
17
+ "CustomerPONumber": null,
18
+ "TaxAmount": 0,
19
+ "ShippingAmount": 0,
20
+ "Billing": {
21
+ "Address1": "3128 N Broadway",
22
+ "Address2": "Upstairs",
23
+ "City": "Chicago",
24
+ "State": "IL",
25
+ "Postal": "60657",
26
+ "Phone": "123123123",
27
+ "Tag": "Doe"
28
+ },
29
+ "ShippingAddressID": 0,
30
+ "Shipping": null,
31
+ "CustomerID": 11843,
32
+ "CustomerName": "John Doe",
33
+ "LineItems": null,
34
+ "ID": 14240,
35
+ "Success": true,
36
+ "Message": "Payment Method Added"
37
+ }