solidus_signifyd 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.rspec +1 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +10 -0
  6. data/LICENSE +26 -0
  7. data/README.md +32 -0
  8. data/Rakefile +21 -0
  9. data/app/controllers/spree/api/spree_signifyd/orders_controller.rb +54 -0
  10. data/app/models/spree/order_decorator.rb +1 -0
  11. data/app/models/spree/signifyd_configuration.rb +6 -0
  12. data/app/models/spree_signifyd/order_concerns.rb +23 -0
  13. data/app/models/spree_signifyd/order_score.rb +9 -0
  14. data/app/models/spree_signifyd/shipment_decorator.rb +11 -0
  15. data/app/overrides/admin_order_signifyd_risk_analysis.rb +6 -0
  16. data/app/serializers/spree_signifyd/address_serializer.rb +20 -0
  17. data/app/serializers/spree_signifyd/billing_address_serializer.rb +13 -0
  18. data/app/serializers/spree_signifyd/credit_card_serializer.rb +25 -0
  19. data/app/serializers/spree_signifyd/delivery_address_serializer.rb +14 -0
  20. data/app/serializers/spree_signifyd/line_item_serializer.rb +25 -0
  21. data/app/serializers/spree_signifyd/order_serializer.rb +59 -0
  22. data/app/serializers/spree_signifyd/user_serializer.rb +44 -0
  23. data/app/views/spree/admin/orders/_signifyd_score.html.erb +10 -0
  24. data/bin/rails +7 -0
  25. data/circle.yml +6 -0
  26. data/config/locales/en.yml +7 -0
  27. data/config/routes.rb +7 -0
  28. data/db/migrate/20140819203000_add_signifyd_score_to_orders.rb +5 -0
  29. data/db/migrate/20140826202644_set_considered_risky_default_value.rb +5 -0
  30. data/db/migrate/20150206151312_create_spree_signifyd_order_scores.rb +11 -0
  31. data/db/migrate/20150206193231_transfer_spree_orders_signifyd_score_data.rb +15 -0
  32. data/db/migrate/20150211202803_remove_spree_orders_signifyd_score_column.rb +5 -0
  33. data/lib/generators/solidus_signifyd/install/install_generator.rb +26 -0
  34. data/lib/generators/solidus_signifyd/install/templates/solidus_signifyd.rb +3 -0
  35. data/lib/solidus_signifyd.rb +1 -0
  36. data/lib/spree_signifyd.rb +37 -0
  37. data/lib/spree_signifyd/create_signifyd_case.rb +13 -0
  38. data/lib/spree_signifyd/engine.rb +24 -0
  39. data/lib/spree_signifyd/request_verifier.rb +14 -0
  40. data/solidus_signifyd.gemspec +36 -0
  41. data/spec/controllers/spree/api/spree_signifyd/orders_controller_spec.rb +202 -0
  42. data/spec/lib/spree_signifyd/create_signifyd_case_spec.rb +20 -0
  43. data/spec/lib/spree_signifyd/request_verifier_spec.rb +27 -0
  44. data/spec/lib/spree_signifyd_spec.rb +66 -0
  45. data/spec/models/spree/order_spec.rb +51 -0
  46. data/spec/models/spree/shipment_spec.rb +59 -0
  47. data/spec/serializers/spree_signifyd/address_serializer_spec.rb +42 -0
  48. data/spec/serializers/spree_signifyd/billing_address_serializer.rb +12 -0
  49. data/spec/serializers/spree_signifyd/credit_card_serializer_spec.rb +26 -0
  50. data/spec/serializers/spree_signifyd/delivery_address_serializer_spec.rb +13 -0
  51. data/spec/serializers/spree_signifyd/line_item_serializer_spec.rb +26 -0
  52. data/spec/serializers/spree_signifyd/order_serializer_spec.rb +72 -0
  53. data/spec/serializers/spree_signifyd/user_serializer_spec.rb +53 -0
  54. data/spec/spec_helper.rb +63 -0
  55. metadata +294 -0
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ module SpreeSignifyd
4
+ describe CreateSignifydCase do
5
+ describe "#perform" do
6
+ let(:order) { create(:order_ready_to_ship) }
7
+ let(:json) { JSON.parse(OrderSerializer.new(order).to_json) }
8
+
9
+ it "calls Signifyd::Case#create with the correct params" do
10
+ Signifyd::Case.should_receive(:create).with(json, SpreeSignifyd::Config[:api_key])
11
+ CreateSignifydCase.perform(order.id)
12
+ end
13
+
14
+ it "calls Signifyd::Case#create with the correct params" do
15
+ Signifyd::Case.should_receive(:create).with(json, SpreeSignifyd::Config[:api_key])
16
+ CreateSignifydCase.perform(order.number)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ module SpreeSignifyd
4
+ describe RequestVerifier do
5
+ include RequestVerifier
6
+
7
+ describe "#encode_request" do
8
+ context "request has special characters" do
9
+ it "returns an unescaped UTF-8 string" do
10
+ expect(encode_request("R\xE9n\xE9 Pe\xF1a")).to eq "Réné Peña"
11
+ end
12
+ end
13
+
14
+ context "request doesn't contain special characters" do
15
+ it "returns the original string" do
16
+ expect(encode_request("John Doe")).to eq "John Doe"
17
+ end
18
+ end
19
+ end
20
+
21
+ describe "#build_sha" do
22
+ it "returns an HMAC SHA256 encoded message" do
23
+ expect(build_sha('ABCDE', 'test')).to eq "K0y2rIeTA77lBEHP8cRPk64fVRbhMrZqEk7la39EjEM="
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+
3
+ module SpreeSignifyd
4
+ describe SpreeSignifyd do
5
+
6
+ describe ".set_score" do
7
+
8
+ let(:order) { FactoryGirl.create(:order) }
9
+
10
+ def set_score(score)
11
+ SpreeSignifyd.set_score(order: order, score: score)
12
+ end
13
+
14
+ it 'creates or updates the score' do
15
+ expect {
16
+ set_score(100)
17
+ }.to change { SpreeSignifyd::OrderScore.count }.by(1)
18
+
19
+ expect(order.signifyd_order_score.score).to eq 100
20
+
21
+ expect {
22
+ set_score(200)
23
+ }.not_to change { SpreeSignifyd::OrderScore.count }
24
+
25
+ expect(order.signifyd_order_score.score).to eq 200
26
+ end
27
+
28
+ end
29
+
30
+ describe ".approve" do
31
+
32
+ let(:order) { FactoryGirl.create(:order_ready_to_ship, line_items_count: 1) }
33
+
34
+ before do
35
+ order.shipments.each { |shipment| shipment.update_attributes!(state: 'pending') }
36
+ order.updater.update_shipment_state
37
+ end
38
+
39
+ def approve
40
+ SpreeSignifyd.approve(order: order)
41
+ end
42
+
43
+ context "updates the order" do
44
+ it { expect { approve }.to change { order.approver_name }.to "SpreeSignifyd" }
45
+ it { expect { approve }.to change { order.shipment_state }.to 'ready' }
46
+ it do
47
+ expect(order.approved_at).to eq nil
48
+ expect { approve }.to change { order.approved_at }
49
+ end
50
+ end
51
+
52
+ it 'readies all of the shipments' do
53
+ order.shipments.each { |shipment| shipment.should_receive(:ready!) }
54
+ approve
55
+ end
56
+ end
57
+
58
+ describe ".create_case" do
59
+ it 'enqueues in resque' do
60
+ expect(Resque).to receive(:enqueue).with(SpreeSignifyd::CreateSignifydCase, 111)
61
+ SpreeSignifyd.create_case(order_number: 111)
62
+ end
63
+ end
64
+
65
+ end
66
+ end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::Order, :type => :model do
4
+
5
+ let!(:order) { create(:order_ready_to_ship, line_items_count: 1) }
6
+
7
+ before do
8
+ order.shipments.each { |shipment| shipment.update_attributes!(state: 'pending') }
9
+ order.updater.update_shipment_state
10
+ end
11
+
12
+ describe "#is_risky?" do
13
+ subject { order.is_risky? }
14
+
15
+ context "no signifyd_score" do
16
+ it { should eq false }
17
+ end
18
+
19
+ context "signifyd_score present" do
20
+ before { SpreeSignifyd.set_score(score: 500, order: order) }
21
+
22
+ context "approved" do
23
+ before { SpreeSignifyd.approve(order: order) }
24
+ it { should eq false }
25
+ end
26
+
27
+ context "not approved" do
28
+ it { should eq true }
29
+ end
30
+ end
31
+ end
32
+
33
+ describe "transition to complete" do
34
+ let(:order) { create(:order_with_line_items, state: 'confirm') }
35
+ let!(:payment) { create(:payment, amount: order.total, order: order ) }
36
+
37
+ it "calls #create_signifyd_case" do
38
+ expect(SpreeSignifyd).to receive(:create_case).with(order_number: order.number)
39
+ order.complete!
40
+ end
41
+
42
+ context "the order is already approved" do # e.g. unreturned exchanges are automatically approved
43
+ it "does not create a case" do
44
+ order.contents.approve(user: Spree.user_class.first)
45
+ expect(SpreeSignifyd).not_to receive(:create_case)
46
+ order.complete!
47
+ end
48
+ end
49
+ end
50
+
51
+ end
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::Shipment, :type => :model do
4
+
5
+ let(:shipment) { create(:shipment) }
6
+ subject { shipment.determine_state(shipment.order) }
7
+
8
+ describe "#determine_state_with_signifyd" do
9
+
10
+ context "with a risky order" do
11
+ before { shipment.order.stub(:is_risky?).and_return(true) }
12
+
13
+ context "the order is not approved" do
14
+ it "returns pending" do
15
+ shipment.order.stub(:approved?).and_return(false)
16
+ subject.should eq "pending"
17
+ end
18
+ end
19
+
20
+ context "the order is approved" do
21
+ it "defaults to existing behavior" do
22
+ shipment.order.stub(:approved?).and_return(true)
23
+ shipment.should_receive(:determine_state).with(shipment.order)
24
+ subject
25
+ end
26
+ end
27
+ end
28
+
29
+ context "without a risky order" do
30
+ before { shipment.order.stub(:is_risky?).and_return(false) }
31
+
32
+ it "defaults to existing behavior" do
33
+ shipment.should_receive(:determine_state).with(shipment.order)
34
+ subject
35
+ end
36
+ end
37
+
38
+ context "shipment state" do
39
+ [:shipped, :ready].each do |state|
40
+ context "the shipment is #{state}" do
41
+ before { shipment.update_columns(state: state) }
42
+ it "defaults to existing behavior" do
43
+ shipment.should_receive(:determine_state).with(shipment.order)
44
+ subject
45
+ end
46
+ end
47
+ end
48
+
49
+ [:pending, :canceled].each do |state|
50
+ context "the shipment is #{state}" do
51
+ before { shipment.update_columns(state: state) }
52
+ it "is pending" do
53
+ subject.should eq "pending"
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ module SpreeSignifyd
4
+ describe AddressSerializer do
5
+ let(:ship_address) { create(:address) }
6
+ let(:serialized_address) { JSON.parse(AddressSerializer.new(ship_address).to_json) }
7
+
8
+ context "node values" do
9
+ let(:address) { serialized_address['address'] }
10
+
11
+ it "streetAddress" do
12
+ expect(address['streetAddress']).to eq ship_address.address1
13
+ end
14
+
15
+ it "unit" do
16
+ expect(address['unit']).to eq ship_address.address2
17
+ end
18
+
19
+ it "city" do
20
+ expect(address['city']).to eq ship_address.city
21
+ end
22
+
23
+ describe "provinceCode" do
24
+ it "with a state entity associated" do
25
+ expect(address['provinceCode']).to eq ship_address.state.abbr
26
+ end
27
+ it "with a state_name and no state entity" do
28
+ ship_address.update_attributes!(state_name: ship_address.state.name, state_id: nil)
29
+ expect(address['provinceCode']).to eq ship_address.state_name
30
+ end
31
+ end
32
+
33
+ it "postalCode" do
34
+ expect(address['postalCode']).to eq ship_address.zipcode
35
+ end
36
+
37
+ it "countryCode" do
38
+ expect(address['countryCode']).to eq ship_address.country.iso
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ module SpreeSignifyd
4
+ describe BillingAddressSerializer do
5
+ let(:bill_address) { create(:address) }
6
+ let(:serialized_address) { JSON.parse(BillingAddressSerializer.new(bill_address).to_json) }
7
+
8
+ context "node values" do
9
+ it { serialized_address.should include 'billingAddress' }
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ module SpreeSignifyd
4
+ describe CreditCardSerializer do
5
+ let(:credit_card) { create(:credit_card) }
6
+ let(:serialized_credit_card) { JSON.parse(CreditCardSerializer.new(credit_card).to_json) }
7
+
8
+ context "node values" do
9
+ it "cardHolderName" do
10
+ expect(serialized_credit_card['cardHolderName']).to eq "#{credit_card.first_name} #{credit_card.last_name}"
11
+ end
12
+
13
+ it "last4" do
14
+ expect(serialized_credit_card['last4']).to eq credit_card.last_digits
15
+ end
16
+
17
+ it "expiryMonth" do
18
+ expect(serialized_credit_card['expiryMonth']).to eq credit_card.month
19
+ end
20
+
21
+ it "expiryYear" do
22
+ expect(serialized_credit_card['expiryYear']).to eq credit_card.year
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ module SpreeSignifyd
4
+ describe DeliveryAddressSerializer do
5
+ let(:delivery_address) { create(:address) }
6
+ let(:serialized_address) { JSON.parse(DeliveryAddressSerializer.new(delivery_address).to_json) }
7
+
8
+ context "node values" do
9
+ it { serialized_address.should include 'deliveryAddress' }
10
+ it { serialized_address['fullName'].should eq delivery_address.full_name }
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ module SpreeSignifyd
4
+ describe LineItemSerializer do
5
+ let(:line_item) { create(:line_item) }
6
+ let(:serialized_line_item) { JSON.parse(LineItemSerializer.new(line_item).to_json) }
7
+
8
+ context "node values" do
9
+ it "itemId" do
10
+ expect(serialized_line_item['itemId']).to eq line_item.variant_id
11
+ end
12
+
13
+ it "itemName" do
14
+ expect(serialized_line_item['itemName']).to eq line_item.name
15
+ end
16
+
17
+ it "itemQuantity" do
18
+ expect(serialized_line_item['itemQuantity']).to eq line_item.quantity
19
+ end
20
+
21
+ it "itemPrice" do
22
+ expect(serialized_line_item['itemPrice']).to eq line_item.price.to_s
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+
3
+ module SpreeSignifyd
4
+ describe OrderSerializer do
5
+ let(:order) { create(:shipped_order, line_items_count: 1) }
6
+ let(:line_item) { order.line_items.first }
7
+ let(:serialized_order) { JSON.parse(OrderSerializer.new(order).to_json) }
8
+
9
+ describe "node values" do
10
+ context "purchase" do
11
+
12
+ let(:purchase) { serialized_order['purchase'] }
13
+
14
+ it { purchase['browserIpAddress'].should eq order.last_ip_address }
15
+ it { purchase['orderId'].should eq order.number }
16
+ it { purchase['createdAt'].should eq order.completed_at.utc.iso8601 }
17
+ it { purchase['currency'].should eq order.currency }
18
+ it { purchase['totalPrice'].should eq order.total.to_s }
19
+
20
+ context "with a payment" do
21
+ it { purchase['avsResponseCode'].should eq order.payments.last.avs_response }
22
+ it { purchase['cvvResponseCode'].should eq order.payments.last.cvv_response_code }
23
+ end
24
+
25
+ context "without a payment" do
26
+ let(:order) { create(:completed_order_with_totals) }
27
+
28
+ it { purchase['avsResponseCode'].should be nil }
29
+ it { purchase['cvvResponseCode'].should be nil }
30
+ end
31
+
32
+ it "contains a products node" do
33
+ purchase['products'].should eq [ JSON.parse(SpreeSignifyd::LineItemSerializer.new(line_item).to_json) ]
34
+ end
35
+ end
36
+
37
+ context "userAccount" do
38
+ it { serialized_order.should include 'userAccount' }
39
+ end
40
+
41
+ context "recipient" do
42
+ it { serialized_order.should include 'recipient' }
43
+ it { serialized_order["recipient"]["confirmationEmail"].should eq order.email }
44
+ end
45
+
46
+ context "card" do
47
+ it { serialized_order.should include 'card' }
48
+
49
+ context "credit card payment" do
50
+ let!(:payment) { create(:payment, order: order) }
51
+
52
+ it { serialized_order["card"].should include 'billingAddress'}
53
+ end
54
+
55
+ context "no payment source" do
56
+ let(:order) { create(:completed_order_with_totals) }
57
+
58
+ it "contains no data" do
59
+ expect(serialized_order["card"]).to eq({})
60
+ end
61
+ end
62
+
63
+ context "non credit card payment" do
64
+ it "contains no data" do
65
+ Spree::CreditCard.any_instance.stub(:instance_of?).and_return(false)
66
+ expect(serialized_order["card"]).to eq({})
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+
3
+ module SpreeSignifyd
4
+ describe UserSerializer do
5
+ let(:user) { create(:user) }
6
+ let!(:incomplete_order) { create(:order, user: user) }
7
+ let!(:old_complete_order) { create(:shipped_order, user: user, created_at: 30.days.ago) }
8
+ let!(:new_complete_order) { create(:shipped_order, user: user) }
9
+
10
+ let(:serialized_user) { JSON.parse(UserSerializer.new(user).to_json) }
11
+
12
+ context "node values" do
13
+ it "emailAddress" do
14
+ expect(serialized_user['emailAddress']).to eq user.email
15
+ end
16
+
17
+ it "username" do
18
+ expect(serialized_user['username']).to eq user.email
19
+ end
20
+
21
+ it "createdDate" do
22
+ expect(serialized_user['createdDate']).to eq user.created_at.utc.iso8601
23
+ end
24
+
25
+ it "lastUpdateDate" do
26
+ expect(serialized_user['lastUpdateDate']).to eq user.updated_at.utc.iso8601
27
+ end
28
+
29
+ context "with at least 2 orders" do
30
+ it "lastOrderId" do
31
+ expect(serialized_user['lastOrderId']).to eq old_complete_order.number
32
+ end
33
+ end
34
+
35
+ context "without prior orders" do
36
+ let(:user_without_orders) { create(:user) }
37
+ let(:serialized_user) { JSON.parse(UserSerializer.new(user_without_orders).to_json) }
38
+
39
+ it "lastOrderId" do
40
+ expect(serialized_user['lastOrderId']).to eq nil
41
+ end
42
+ end
43
+
44
+ it "aggregateOrderCount" do
45
+ expect(serialized_user['aggregateOrderCount']).to eq 2
46
+ end
47
+
48
+ it "aggregateOrderDollars" do
49
+ expect(serialized_user['aggregateOrderDollars']).to eq (old_complete_order.total + new_complete_order.total).to_s
50
+ end
51
+ end
52
+ end
53
+ end