spree_bitpay 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +24 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +18 -0
  5. data/Gemfile +7 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +79 -0
  8. data/Rakefile +21 -0
  9. data/app/assets/images/BC_nBG_64px.png +0 -0
  10. data/app/assets/images/bitcoin.png +0 -0
  11. data/app/assets/javascripts/easyModal.js-master/.gitignore +1 -0
  12. data/app/assets/javascripts/easyModal.js-master/README.md +3 -0
  13. data/app/assets/javascripts/easyModal.js-master/bower.json +20 -0
  14. data/app/assets/javascripts/easyModal.js-master/jquery.easyModal.js +161 -0
  15. data/app/assets/javascripts/spree/backend/spree_bitpay.js +3 -0
  16. data/app/assets/javascripts/spree/frontend/spree_bitpay.js +86 -0
  17. data/app/assets/stylesheets/spree/backend/spree_bitpay.css +3 -0
  18. data/app/assets/stylesheets/spree/frontend/spree_bitpay.css +58 -0
  19. data/app/controllers/spree/bitpay_controller.rb +248 -0
  20. data/app/models/spree/bitpay_invoice.rb +23 -0
  21. data/app/models/spree/payment_method/bitpay.rb +64 -0
  22. data/app/overrides/spree/payments/_payment/bitpay_payment_params.html.erb.deface +55 -0
  23. data/app/views/spree/admin/payments/source_views/_bitpay.html.erb +23 -0
  24. data/app/views/spree/checkout/payment/_bitpay.html.erb +4 -0
  25. data/bin/rails +7 -0
  26. data/config/locales/en.yml +16 -0
  27. data/config/routes.rb +9 -0
  28. data/db/migrate/20140720052959_create_spree_bitpay_invoices.rb +8 -0
  29. data/db/migrate/20140725200946_add_fields_to_spree_bitpay_invoice.rb +14 -0
  30. data/db/migrate/20140729192827_add_index_to_spree_payments.rb +5 -0
  31. data/lib/generators/spree_bitpay/install/install_generator.rb +32 -0
  32. data/lib/spree_bitpay/engine.rb +28 -0
  33. data/lib/spree_bitpay/factories.rb +6 -0
  34. data/lib/spree_bitpay/version.rb +3 -0
  35. data/lib/spree_bitpay.rb +3 -0
  36. data/script/rails +7 -0
  37. data/spec/factories/bitpay_test_factories.rb +54 -0
  38. data/spec/features/bitpay_plugin_spec.rb +75 -0
  39. data/spec/fixtures/valid_confirmed_callback.json +27 -0
  40. data/spec/fixtures/valid_confirmed_invoice.json +15 -0
  41. data/spec/fixtures/valid_expired_invoice.json +16 -0
  42. data/spec/fixtures/valid_invalid_callback.json +27 -0
  43. data/spec/fixtures/valid_invalid_invoice.json +15 -0
  44. data/spec/fixtures/valid_new_invoice.json +15 -0
  45. data/spec/fixtures/valid_overpaid_callback.json +27 -0
  46. data/spec/fixtures/valid_overpaid_invoice.json +15 -0
  47. data/spec/fixtures/valid_paid_callback.json +27 -0
  48. data/spec/fixtures/valid_paid_invoice.json +15 -0
  49. data/spec/models/spree/payment_method/bitpay.rb +27 -0
  50. data/spec/requests/notifications_spec.rb +218 -0
  51. data/spec/spec_helper.rb +131 -0
  52. data/spree_bitpay.gemspec +41 -0
  53. data/testapp.sh +16 -0
  54. metadata +375 -0
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+
3
+ feature "Bitpay Plugin", js: true, type: :feature do
4
+
5
+ # NOTE: Tests require a properly populated test DB as per testapp.sh.
6
+ # Not all tests are idempotent - DB may require manual reset after test failure
7
+
8
+ scenario "can be configured by admin" do
9
+
10
+ admin = create(:admin_user, email: "test@bitpay.com")
11
+
12
+ visit admin_login_path
13
+ fill_in 'Email', with: admin.email
14
+ fill_in 'Password', with: 'secret'
15
+ click_button "Login"
16
+ visit new_admin_payment_method_path
17
+ # Should show up as a provider
18
+ expect(page).to have_selector('#gtwy-type option[value="Spree::PaymentMethod::Bitpay"]'), "Not visible in drop down"
19
+
20
+ fill_in "payment_method_name", with: "Bitcoin"
21
+ fill_in "payment_method_description", with: "BitPay payment processing"
22
+ select "Spree::PaymentMethod::Bitpay", from: "gtwy-type"
23
+ # Should create a new PaymentMethod, redirect to edit page and flash success message
24
+ expect { click_on "Create" }.to change(Spree::PaymentMethod, :count).by(1)
25
+ #expect(response).to be_redirect
26
+ expect(page).to have_selector('.success'), "No success message"
27
+
28
+ # should have bitpay production address as default endpoint
29
+ # (it does but somehow previous inputs get cached and take precedence??)
30
+ # expect(page).to have_selector('#payment_method_bitpay_preferred_api_endpoint[value="https://bitpay.com/api"]')
31
+
32
+ fill_in "payment_method_bitpay_preferred_api_endpoint", with: "https://test.bitpay.com/api"
33
+ fill_in "payment_method_bitpay_preferred_api_key", with: ENV['BITPAYKEY']
34
+ select "Test", from: "gtwy-env"
35
+
36
+ click_on "Update"
37
+ expect(page).to have_selector('.success'), "No success message"
38
+ visit admin_logout_path
39
+
40
+ end
41
+
42
+ xscenario "can display invoice" do
43
+ user = create(:user_with_addreses)
44
+ shipping_method = create(:free_shipping_method, name: "Satoshi Post")
45
+ product = create(:base_product, name: "BitPay T-Shirt")
46
+ visit login_path
47
+ fill_in 'Email', with: user.email
48
+ fill_in 'Password', with: 'secret'
49
+ click_button "Login"
50
+
51
+ expect(current_path).to eq(root_path), "User Login failed"
52
+ click_on "BitPay T-Shirt"
53
+ click_button "Add To Cart"
54
+ click_button "Checkout"
55
+ click_button "Save and Continue" # Confirm Address
56
+ click_button "Save and Continue" # Confirm Delivery Options
57
+ choose "Bitcoin"
58
+
59
+ #TODO expect image is visible
60
+
61
+ expect { click_button "Save and Continue" }.to change(Spree::BitpayInvoice, :count).by(1) # Confirm Payment Options
62
+ expect(current_path).to end_with "confirm"
63
+
64
+ click_button "Place Order"
65
+
66
+ page.within_frame 'bitpay_invoice_iframe' do
67
+ expect(page).to have_content("Pay with Bitcoin")
68
+ end
69
+
70
+
71
+ #save_and_open_screenshot
72
+
73
+ end
74
+
75
+ end
@@ -0,0 +1,27 @@
1
+ {"id":"123BitPayInvoiceID",
2
+ "url":"https://localhost:8088/invoice?id=123BitPayInvoiceID",
3
+ "posData":"{\"paymentID\":\"123PAYMENTID\",\"orderID\":\"123ORDERID\"}",
4
+ "status":"confirmed",
5
+ "btcPrice":"0.0512",
6
+ "price":29.14,
7
+ "currency":"USD",
8
+ "invoiceTime":1407881291063,
9
+ "expirationTime":1407882191063,
10
+ "currentTime":1407882058099,
11
+ "btcPaid":"0.0512",
12
+ "rate":568.69,
13
+ "exceptionStatus":false,
14
+ "bitpay":
15
+ {"id":"123BitPayInvoiceID",
16
+ "url":"https://localhost:8088/invoice?id=123BitPayInvoiceID",
17
+ "posData":"{\"paymentID\":\"123PAYMENTID\",\"orderID\":\"123ORDERID\"}",
18
+ "status":"confirmed",
19
+ "btcPrice":"0.0512",
20
+ "price":29.14,
21
+ "currency":"USD",
22
+ "invoiceTime":1407881291063,
23
+ "expirationTime":1407882191063,
24
+ "currentTime":1407882058099,
25
+ "btcPaid":"0.0512",
26
+ "rate":568.69,
27
+ "exceptionStatus":false}}
@@ -0,0 +1,15 @@
1
+ {
2
+ "id": "123BitPayInvoiceID",
3
+ "url": "https://localhost:8088/invoice?id=123BitPayInvoiceID",
4
+ "posData": "{\"paymentID\":\"123PAYMENTID\",\"orderID\":\"123ORDERID\"}",
5
+ "status": "confirmed",
6
+ "btcPrice": "0.0720",
7
+ "price": 38.58,
8
+ "currency": "USD",
9
+ "invoiceTime": 1407988706154,
10
+ "expirationTime": 1407989606154,
11
+ "currentTime": 1407988785613,
12
+ "btcPaid": "0.0720",
13
+ "rate": 536.19,
14
+ "exceptionStatus": false
15
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "id": "HA1CyVydbwLmB6LS35RNQE",
3
+ "url": "https://paul.bp:8088/invoice?id=HA1CyVydbwLmB6LS35RNQE",
4
+ "posData": "{\"paymentID\":\"EDY7PJWM\",\"orderID\":\"R337975673\"}",
5
+ "status": "expired",
6
+ "btcPrice": "0.0648",
7
+ "price": 29.14,
8
+ "currency": "USD",
9
+ "invoiceTime": 1411008484489,
10
+ "expirationTime": 1411009384489,
11
+ "currentTime": 1411051760821,
12
+ "btcPaid": "0.0000",
13
+ "rate": 450,
14
+ "exceptionStatus": false,
15
+ "buyerFields": {}
16
+ }
@@ -0,0 +1,27 @@
1
+ {"id":"123BitPayInvoiceID",
2
+ "url":"https://localhost:8088/invoice?id=123BitPayInvoiceID",
3
+ "posData":"{\"paymentID\":\"123PAYMENTID\",\"orderID\":\"123ORDERID\"}",
4
+ "status":"invalid",
5
+ "btcPrice":"0.0512",
6
+ "price":29.14,
7
+ "currency":"USD",
8
+ "invoiceTime":1407881291063,
9
+ "expirationTime":1407882191063,
10
+ "currentTime":1407882058099,
11
+ "btcPaid":"0.0512",
12
+ "rate":568.69,
13
+ "exceptionStatus":false,
14
+ "bitpay":
15
+ {"id":"123BitPayInvoiceID",
16
+ "url":"https://localhost:8088/invoice?id=123BitPayInvoiceID",
17
+ "posData":"{\"paymentID\":\"123PAYMENTID\",\"orderID\":\"123ORDERID\"}",
18
+ "status":"invalid",
19
+ "btcPrice":"0.0512",
20
+ "price":29.14,
21
+ "currency":"USD",
22
+ "invoiceTime":1407881291063,
23
+ "expirationTime":1407882191063,
24
+ "currentTime":1407882058099,
25
+ "btcPaid":"0.0512",
26
+ "rate":568.69,
27
+ "exceptionStatus":false}}
@@ -0,0 +1,15 @@
1
+ {
2
+ "id": "123BitPayInvoiceID",
3
+ "url": "https://localhost:8088/invoice?id=123BitPayInvoiceID",
4
+ "posData": "{\"paymentID\":\"123PAYMENTID\",\"orderID\":\"123ORDERID\"}",
5
+ "status": "invalid",
6
+ "btcPrice": "0.0720",
7
+ "price": 38.58,
8
+ "currency": "USD",
9
+ "invoiceTime": 1407988706154,
10
+ "expirationTime": 1407989606154,
11
+ "currentTime": 1407988785613,
12
+ "btcPaid": "0.0720",
13
+ "rate": 536.19,
14
+ "exceptionStatus": false
15
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "id": "123BitPayInvoiceID",
3
+ "url": "https://localhost:8088/invoice?id=123BitPayInvoiceID",
4
+ "posData": "{\"paymentID\":\"123PAYMENTID\",\"orderID\":\"123ORDERID\"}",
5
+ "status": "new",
6
+ "btcPrice": "0.0720",
7
+ "price": 38.58,
8
+ "currency": "USD",
9
+ "invoiceTime": 1407988706154,
10
+ "expirationTime": 1407989606154,
11
+ "currentTime": 1407988785613,
12
+ "btcPaid": "0.0720",
13
+ "rate": 536.19,
14
+ "exceptionStatus": false
15
+ }
@@ -0,0 +1,27 @@
1
+ {"id":"123BitPayInvoiceID",
2
+ "url":"https://localhost:8088/invoice?id=123BitPayInvoiceID",
3
+ "posData":"{\"paymentID\":\"123PAYMENTID\",\"orderID\":\"123ORDERID\"}",
4
+ "status":"paid",
5
+ "btcPrice":"0.0512",
6
+ "price":29.14,
7
+ "currency":"USD",
8
+ "invoiceTime":1407881291063,
9
+ "expirationTime":1407882191063,
10
+ "currentTime":1407882058099,
11
+ "btcPaid":"0.0512",
12
+ "rate":568.69,
13
+ "exceptionStatus":"paidOver",
14
+ "bitpay":
15
+ {"id":"123BitPayInvoiceID",
16
+ "url":"https://localhost:8088/invoice?id=123BitPayInvoiceID",
17
+ "posData":"{\"paymentID\":\"123PAYMENTID\",\"orderID\":\"123ORDERID\"}",
18
+ "status":"confirmed",
19
+ "btcPrice":"0.0512",
20
+ "price":29.14,
21
+ "currency":"USD",
22
+ "invoiceTime":1407881291063,
23
+ "expirationTime":1407882191063,
24
+ "currentTime":1407882058099,
25
+ "btcPaid":"0.0512",
26
+ "rate":568.69,
27
+ "exceptionStatus":"paidOver"}}
@@ -0,0 +1,15 @@
1
+ {
2
+ "id": "123BitPayInvoiceID",
3
+ "url": "https://localhost:8088/invoice?id=123BitPayInvoiceID",
4
+ "posData": "{\"paymentID\":\"123PAYMENTID\",\"orderID\":\"123ORDERID\"}",
5
+ "status": "paid",
6
+ "btcPrice": "0.0720",
7
+ "price": 38.58,
8
+ "currency": "USD",
9
+ "invoiceTime": 1407988706154,
10
+ "expirationTime": 1407989606154,
11
+ "currentTime": 1407988785613,
12
+ "btcPaid": "0.0720",
13
+ "rate": 536.19,
14
+ "exceptionStatus": "paidOver"
15
+ }
@@ -0,0 +1,27 @@
1
+ {"id":"123BitPayInvoiceID",
2
+ "url":"https://localhost:8088/invoice?id=123BitPayInvoiceID",
3
+ "posData":"{\"paymentID\":\"123PAYMENTID\",\"orderID\":\"123ORDERID\"}",
4
+ "status":"paid",
5
+ "btcPrice":"0.0512",
6
+ "price":29.14,
7
+ "currency":"USD",
8
+ "invoiceTime":1407881291063,
9
+ "expirationTime":1407882191063,
10
+ "currentTime":1407882058099,
11
+ "btcPaid":"0.0512",
12
+ "rate":568.69,
13
+ "exceptionStatus":false,
14
+ "bitpay":
15
+ {"id":"123BitPayInvoiceID",
16
+ "url":"https://localhost:8088/invoice?id=123BitPayInvoiceID",
17
+ "posData":"{\"paymentID\":\"123PAYMENTID\",\"orderID\":\"123ORDERID\"}",
18
+ "status":"confirmed",
19
+ "btcPrice":"0.0512",
20
+ "price":29.14,
21
+ "currency":"USD",
22
+ "invoiceTime":1407881291063,
23
+ "expirationTime":1407882191063,
24
+ "currentTime":1407882058099,
25
+ "btcPaid":"0.0512",
26
+ "rate":568.69,
27
+ "exceptionStatus":false}}
@@ -0,0 +1,15 @@
1
+ {
2
+ "id": "123BitPayInvoiceID",
3
+ "url": "https://localhost:8088/invoice?id=123BitPayInvoiceID",
4
+ "posData": "{\"paymentID\":\"123PAYMENTID\",\"orderID\":\"123ORDERID\"}",
5
+ "status": "paid",
6
+ "btcPrice": "0.0720",
7
+ "price": 38.58,
8
+ "currency": "USD",
9
+ "invoiceTime": 1407988706154,
10
+ "expirationTime": 1407989606154,
11
+ "currentTime": 1407988785613,
12
+ "btcPaid": "0.0720",
13
+ "rate": 536.19,
14
+ "exceptionStatus": false
15
+ }
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::PaymentMethod::Bitpay do
4
+ describe '#scan_the_server' do
5
+ subject = FactoryGirl.create(:bitcoin_payment_method)
6
+ it{ respond_to :scan_the_server }
7
+
8
+ context 'when the invoice does not exist' do
9
+ it "returns 'invoice not found'" do
10
+ expect_any_instance_of(BitPay::Client).to receive(:get).and_return( { "error"=> { "type"=>"notFound", "message"=>"Invoice not found"} } )
11
+ expect(subject.scan_the_server("5")).to eq("Invoice not found")
12
+ end
13
+ end
14
+
15
+ context 'when the invoice has expired or paid' do
16
+ it "returns 'expired' for expired invoices" do
17
+ expect_any_instance_of(BitPay::Client).to receive(:get).and_return(get_fixture("valid_expired_invoice.json"))
18
+ expect(subject.scan_the_server("5")).to eq("expired")
19
+ end
20
+
21
+ it "returns 'paid' for paid invoices" do
22
+ expect_any_instance_of(BitPay::Client).to receive(:get).and_return(get_fixture("valid_paid_invoice.json"))
23
+ expect(subject.scan_the_server("5")).to eq("paid")
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,218 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Notifications" do
4
+ it "rejects malformed notifications" do
5
+
6
+ # Send malformed notification callback
7
+ callback_body = "THIS IS OBVIOUSLY NOT VALID JSON"
8
+ post bitpay_notification_path, callback_body
9
+
10
+ # Expect malformed response code
11
+ expect(response).to have_http_status(:unprocessable_entity)
12
+ end
13
+
14
+ it "Handles 'paid' notification" do
15
+
16
+ order = create(:processing_payment_with_confirming_order).order
17
+ payment = order.payments.first
18
+
19
+ # Validate Starting State
20
+ expect(order.state).to eq("confirm")
21
+ expect(payment.state).to eq("processing")
22
+
23
+ # Send 'paid' notification callback
24
+ callback_body = get_fixture("valid_paid_callback.json")
25
+ invoice = get_fixture("valid_paid_invoice.json")
26
+
27
+ stub_request(:get, "https://bitpay.com/api/invoice/123BitPayInvoiceID").
28
+ to_return(:status => 200, :body => invoice.to_json)
29
+
30
+ post bitpay_notification_path, callback_body
31
+
32
+ expect(WebMock).to have_requested(:get, "https://bitpay.com/api/invoice/123BitPayInvoiceID")
33
+
34
+ expect(order.reload.state).to eq("complete")
35
+ expect(payment.reload.state).to eq("pending")
36
+
37
+ end
38
+
39
+ it "Handles 'confirmed' notification" do
40
+
41
+ order = create(:pending_payment_with_complete_order).order
42
+ payment = order.payments.first
43
+
44
+ # Validate Starting State
45
+ expect(order.state).to eq("complete")
46
+ expect(payment.state).to eq("pending")
47
+
48
+ # Send 'paid' notification callback
49
+ callback_body = get_fixture("valid_confirmed_callback.json")
50
+ invoice = get_fixture("valid_confirmed_invoice.json")
51
+
52
+ stub_request(:get, "https://bitpay.com/api/invoice/123BitPayInvoiceID").
53
+ to_return(:status => 200, :body => invoice.to_json)
54
+
55
+ post bitpay_notification_path, callback_body
56
+ expect(WebMock).to have_requested(:get, "https://bitpay.com/api/invoice/123BitPayInvoiceID")
57
+
58
+ expect(order.reload.state).to eq("complete")
59
+ expect(payment.reload.state).to eq("completed")
60
+ end
61
+
62
+ it "Handles Overpayment" do
63
+ # Should behave identically to exact payment
64
+ # Could enhance by adding flag/notification for merchant
65
+
66
+ payment = create(:processing_payment_with_confirming_order)
67
+ order = payment.order
68
+
69
+ # Validate Starting State
70
+ expect(order.state).to eq("confirm")
71
+ expect(order.payments.first.state).to eq("processing")
72
+
73
+ # Send 'paid' notification callback
74
+ callback_body = get_fixture("valid_overpaid_callback.json")
75
+ invoice = get_fixture("valid_overpaid_invoice.json")
76
+
77
+ stub_request(:get, "https://bitpay.com/api/invoice/123BitPayInvoiceID").
78
+ to_return(:status => 200, :body => invoice.to_json)
79
+
80
+ post bitpay_notification_path, callback_body
81
+ expect(WebMock).to have_requested(:get, "https://bitpay.com/api/invoice/123BitPayInvoiceID")
82
+
83
+ expect(order.reload.state).to eq("complete")
84
+ expect(payment.reload.state).to eq("pending")
85
+ end
86
+
87
+ it "Handles Expired Invoices that are later accepted" do
88
+ # An underpaid invoice will not send notifications - however it might be marked expired/paidPartial if the merchant refreshes status
89
+ # In coordination with BitPay support, a merchant can have the invoice marked paid, and it should move from expired to paid properly
90
+
91
+ payment = create(:invalid_payment_with_confirming_order)
92
+ order = payment.order
93
+
94
+ # Validate Starting State
95
+ expect(order.state).to eq("confirm")
96
+ expect(order.payments.first.state).to eq("invalid")
97
+
98
+ invoice = get_fixture("valid_confirmed_invoice.json")
99
+
100
+ stub_request(:get, "https://bitpay.com/api/invoice/123BitPayInvoiceID").
101
+ to_return(:status => 200, :body => invoice.to_json)
102
+
103
+ # Call the "refresh" method
104
+ get bitpay_refresh_path, payment: payment.id
105
+ expect(WebMock).to have_requested(:get, "https://bitpay.com/api/invoice/123BitPayInvoiceID")
106
+
107
+ expect(order.reload.state).to eq("complete")
108
+ expect(payment.reload.state).to eq("completed")
109
+ end
110
+
111
+ it "Handles 'paid' notifications for Payments in 'invalid' state" do
112
+ # If a user somehow pays an invoice after the payment has been marked 'invalid', we should still recover the payment
113
+
114
+ payment = create(:invalid_payment_with_confirming_order)
115
+ order = payment.order
116
+
117
+ # Validate Starting State
118
+ expect(order.state).to eq("confirm")
119
+ expect(order.payments.first.state).to eq("invalid")
120
+
121
+
122
+ # Send 'paid' notification callback
123
+ callback_body = get_fixture("valid_paid_callback.json")
124
+ invoice = get_fixture("valid_paid_invoice.json")
125
+
126
+ stub_request(:get, "https://bitpay.com/api/invoice/123BitPayInvoiceID").
127
+ to_return(:status => 200, :body => invoice.to_json)
128
+
129
+ post bitpay_notification_path, callback_body
130
+ expect(WebMock).to have_requested(:get, "https://bitpay.com/api/invoice/123BitPayInvoiceID")
131
+
132
+ expect(order.reload.state).to eq("complete")
133
+ expect(payment.reload.state).to eq("pending")
134
+ end
135
+
136
+ it "Handles 'invalid' notification" do
137
+ # Should cause payment to move into "failed" state
138
+
139
+ order = create(:pending_payment_with_complete_order).order
140
+ payment = order.payments.first
141
+
142
+ # Validate Starting State
143
+ expect(order.state).to eq("complete")
144
+ expect(payment.state).to eq("pending")
145
+
146
+ # Send 'paid' notification callback
147
+ callback_body = get_fixture("valid_invalid_callback.json")
148
+ invoice = get_fixture("valid_invalid_invoice.json")
149
+
150
+ stub_request(:get, "https://bitpay.com/api/invoice/123BitPayInvoiceID").
151
+ to_return(:status => 200, :body => invoice.to_json)
152
+
153
+ post bitpay_notification_path, callback_body
154
+ expect(WebMock).to have_requested(:get, "https://bitpay.com/api/invoice/123BitPayInvoiceID")
155
+
156
+ expect(order.reload.state).to eq("complete")
157
+ expect(payment.reload.state).to eq("failed")
158
+ end
159
+
160
+ it "Handles non-existent orders" do
161
+ # Should return "unprocessable" code if no order exists
162
+ callback_body = get_fixture("valid_paid_callback.json")
163
+ post bitpay_notification_path, callback_body
164
+
165
+ # Expect malformed response code
166
+ expect(response).to have_http_status(:unprocessable_entity)
167
+
168
+ end
169
+
170
+ it "Handles false 'paid' notifications" do
171
+ # Should not change order
172
+ order = create(:processing_payment_with_confirming_order).order
173
+ payment = order.payments.first
174
+
175
+ # Validate Starting State
176
+ expect(order.state).to eq("confirm")
177
+ expect(payment.state).to eq("processing")
178
+
179
+ # Send 'paid' notification callback, but invoice is in 'new' state
180
+ callback_body = get_fixture("valid_paid_callback.json")
181
+ invoice = get_fixture("valid_new_invoice.json")
182
+
183
+ stub_request(:get, "https://bitpay.com/api/invoice/123BitPayInvoiceID").
184
+ to_return(:status => 200, :body => invoice.to_json)
185
+
186
+ post bitpay_notification_path, callback_body
187
+
188
+ expect(WebMock).to have_requested(:get, "https://bitpay.com/api/invoice/123BitPayInvoiceID")
189
+
190
+ expect(order.reload.state).to eq("confirm")
191
+ expect(payment.reload.state).to eq("processing")
192
+ end
193
+
194
+ it "Handles false 'confirmed' notifications" do
195
+ # Should not change order
196
+ order = create(:processing_payment_with_confirming_order).order
197
+ payment = order.payments.first
198
+
199
+ # Validate Starting State
200
+ expect(order.state).to eq("confirm")
201
+ expect(payment.state).to eq("processing")
202
+
203
+ # Send 'confirmed' notification callback, but invoice is in 'new' state
204
+ callback_body = get_fixture("valid_confirmed_callback.json")
205
+ invoice = get_fixture("valid_new_invoice.json")
206
+
207
+ stub_request(:get, "https://bitpay.com/api/invoice/123BitPayInvoiceID").
208
+ to_return(:status => 200, :body => invoice.to_json)
209
+
210
+ post bitpay_notification_path, callback_body
211
+
212
+ expect(WebMock).to have_requested(:get, "https://bitpay.com/api/invoice/123BitPayInvoiceID")
213
+
214
+ expect(order.reload.state).to eq("confirm")
215
+ expect(payment.reload.state).to eq("processing")
216
+ end
217
+
218
+ end
@@ -0,0 +1,131 @@
1
+ # Run Coverage report
2
+ require 'simplecov'
3
+ SimpleCov.start do
4
+ add_filter 'spec/dummy'
5
+ add_group 'Controllers', 'app/controllers'
6
+ add_group 'Helpers', 'app/helpers'
7
+ add_group 'Mailers', 'app/mailers'
8
+ add_group 'Models', 'app/models'
9
+ add_group 'Views', 'app/views'
10
+ add_group 'Libraries', 'lib'
11
+ end
12
+
13
+ # Configure Rails Environment
14
+ ENV['RAILS_ENV'] = 'test'
15
+
16
+ require File.expand_path('../dummy/config/environment.rb', __FILE__)
17
+
18
+ require 'rspec/rails'
19
+ require 'capybara/rspec'
20
+ require 'webmock/rspec'
21
+ require 'database_cleaner'
22
+ require 'ffaker'
23
+ require 'pry'
24
+
25
+ # Requires supporting ruby files with custom matchers and macros, etc,
26
+ # in spec/support/ and its subdirectories.
27
+ Dir[File.join(File.dirname(__FILE__), 'support/**/*.rb')].each { |f| require f }
28
+
29
+ # Requires factories and other useful helpers defined in spree_core.
30
+ require 'spree/testing_support/authorization_helpers'
31
+ require 'spree/testing_support/capybara_ext'
32
+ require 'spree/testing_support/controller_requests'
33
+ require 'spree/testing_support/factories'
34
+ require 'spree/testing_support/url_helpers'
35
+
36
+ # Requires factories defined in lib/spree_bitpay/factories.rb
37
+ require 'spree_bitpay/factories'
38
+
39
+ # Require factories under spec/factories
40
+ Dir["#{File.dirname(__FILE__)}/factories/**"].each do |f|
41
+ require File.expand_path(f)
42
+ end
43
+
44
+ # Use Poltergeist driver for compatibility with Travis CI, and tell it to ignore JS errors
45
+ require 'capybara/poltergeist'
46
+ Capybara.register_driver :poltergeist do |app|
47
+ Capybara::Poltergeist::Driver.new(app, :js_errors => false)
48
+ end
49
+ Capybara.javascript_driver = :poltergeist
50
+
51
+ RSpec.configure do |config|
52
+
53
+ # Deprecation Stuff
54
+ config.expose_current_running_example_as :example
55
+ config.infer_spec_type_from_file_location!
56
+
57
+ config.include FactoryGirl::Syntax::Methods
58
+
59
+ # == URL Helpers
60
+ #
61
+ # Allows access to Spree's routes in specs:
62
+ #
63
+ # visit spree.admin_path
64
+ # current_path.should eql(spree.products_path)
65
+ config.include Spree::TestingSupport::UrlHelpers
66
+ config.include Spree::Core::Engine.routes.url_helpers
67
+
68
+ # == Mock Framework
69
+ #
70
+ # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
71
+ #
72
+ # config.mock_with :mocha
73
+ # config.mock_with :flexmock
74
+ # config.mock_with :rr
75
+ config.mock_with :rspec
76
+ config.color = true
77
+
78
+ # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
79
+ # config.fixture_path = "#{::Rails.root}/spec/fixtures"
80
+
81
+ # Capybara javascript drivers require transactional fixtures set to false, and we use DatabaseCleaner
82
+ # to cleanup after each test instead. Without transactional fixtures set to false the records created
83
+ # to setup a test will be unavailable to the browser, which runs under a separate server instance.
84
+ config.use_transactional_fixtures = false
85
+
86
+ # Ensure Suite is set to use transactions for speed.
87
+ config.before :suite do
88
+ DatabaseCleaner.strategy = :transaction
89
+ DatabaseCleaner.clean_with :truncation
90
+ end
91
+
92
+ config.before :each do
93
+ # Disable Webmock restrictions for feature tests.
94
+ if example.metadata[:type] == :feature
95
+ WebMock.allow_net_connect!
96
+ else
97
+ WebMock.disable_net_connect!
98
+ DatabaseCleaner.clean_with :truncation
99
+ end
100
+
101
+ #DatabaseCleaner.strategy = example.metadata[:js] ? :truncation : :transaction
102
+ #DatabaseCleaner.start
103
+ end
104
+
105
+ # # After each spec clean the database.
106
+ # config.after :each do
107
+ # DatabaseCleaner.clean
108
+ # end
109
+
110
+
111
+ # In event of errors, open page
112
+ config.after do
113
+ if example.metadata[:type] == :feature and example.exception.present?
114
+ save_and_open_screenshot
115
+ end
116
+ end
117
+
118
+ config.fail_fast = true
119
+ #config.fail_fast = ENV['FAIL_FAST'] || false
120
+ #config.order = "random"
121
+ end
122
+
123
+ ####
124
+ # Helper Methods
125
+ ###
126
+
127
+ ## Gets the fixture by name
128
+ #
129
+ def get_fixture(name)
130
+ JSON.parse(File.read(File.expand_path("../fixtures/#{name}", __FILE__)))
131
+ end