arrow_payments 0.1.1

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.
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
+ }