spree_bitpay 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +24 -0
- data/.rspec +1 -0
- data/.travis.yml +18 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +79 -0
- data/Rakefile +21 -0
- data/app/assets/images/BC_nBG_64px.png +0 -0
- data/app/assets/images/bitcoin.png +0 -0
- data/app/assets/javascripts/easyModal.js-master/.gitignore +1 -0
- data/app/assets/javascripts/easyModal.js-master/README.md +3 -0
- data/app/assets/javascripts/easyModal.js-master/bower.json +20 -0
- data/app/assets/javascripts/easyModal.js-master/jquery.easyModal.js +161 -0
- data/app/assets/javascripts/spree/backend/spree_bitpay.js +3 -0
- data/app/assets/javascripts/spree/frontend/spree_bitpay.js +86 -0
- data/app/assets/stylesheets/spree/backend/spree_bitpay.css +3 -0
- data/app/assets/stylesheets/spree/frontend/spree_bitpay.css +58 -0
- data/app/controllers/spree/bitpay_controller.rb +248 -0
- data/app/models/spree/bitpay_invoice.rb +23 -0
- data/app/models/spree/payment_method/bitpay.rb +64 -0
- data/app/overrides/spree/payments/_payment/bitpay_payment_params.html.erb.deface +55 -0
- data/app/views/spree/admin/payments/source_views/_bitpay.html.erb +23 -0
- data/app/views/spree/checkout/payment/_bitpay.html.erb +4 -0
- data/bin/rails +7 -0
- data/config/locales/en.yml +16 -0
- data/config/routes.rb +9 -0
- data/db/migrate/20140720052959_create_spree_bitpay_invoices.rb +8 -0
- data/db/migrate/20140725200946_add_fields_to_spree_bitpay_invoice.rb +14 -0
- data/db/migrate/20140729192827_add_index_to_spree_payments.rb +5 -0
- data/lib/generators/spree_bitpay/install/install_generator.rb +32 -0
- data/lib/spree_bitpay/engine.rb +28 -0
- data/lib/spree_bitpay/factories.rb +6 -0
- data/lib/spree_bitpay/version.rb +3 -0
- data/lib/spree_bitpay.rb +3 -0
- data/script/rails +7 -0
- data/spec/factories/bitpay_test_factories.rb +54 -0
- data/spec/features/bitpay_plugin_spec.rb +75 -0
- data/spec/fixtures/valid_confirmed_callback.json +27 -0
- data/spec/fixtures/valid_confirmed_invoice.json +15 -0
- data/spec/fixtures/valid_expired_invoice.json +16 -0
- data/spec/fixtures/valid_invalid_callback.json +27 -0
- data/spec/fixtures/valid_invalid_invoice.json +15 -0
- data/spec/fixtures/valid_new_invoice.json +15 -0
- data/spec/fixtures/valid_overpaid_callback.json +27 -0
- data/spec/fixtures/valid_overpaid_invoice.json +15 -0
- data/spec/fixtures/valid_paid_callback.json +27 -0
- data/spec/fixtures/valid_paid_invoice.json +15 -0
- data/spec/models/spree/payment_method/bitpay.rb +27 -0
- data/spec/requests/notifications_spec.rb +218 -0
- data/spec/spec_helper.rb +131 -0
- data/spree_bitpay.gemspec +41 -0
- data/testapp.sh +16 -0
- metadata +375 -0
@@ -0,0 +1,248 @@
|
|
1
|
+
module Spree
|
2
|
+
class BitpayController < StoreController
|
3
|
+
skip_before_filter :verify_authenticity_token, :only => [:notification]
|
4
|
+
|
5
|
+
# Generates Bitpay Invoice and returns iframe view
|
6
|
+
#
|
7
|
+
def pay_now
|
8
|
+
|
9
|
+
order = current_order || raise(ActiveRecord::RecordNotFound)
|
10
|
+
|
11
|
+
return redirect_to root_url if order.state != "confirm"
|
12
|
+
|
13
|
+
# Find the payment by searching for valid payments associated with the order
|
14
|
+
# VOID payments are considered valid, so need to exclude those too
|
15
|
+
payments = order.payments.where.not(state: %w(failed invalid void))
|
16
|
+
|
17
|
+
if payments.count > 1 # If there are other completed payments use the one in checkout state
|
18
|
+
payment = payments.where(state: "checkout").first
|
19
|
+
else
|
20
|
+
payment = payments.first
|
21
|
+
end
|
22
|
+
|
23
|
+
logger.debug "Found payment: #{payment.inspect}"
|
24
|
+
|
25
|
+
case payment.state
|
26
|
+
when "checkout"
|
27
|
+
# New checkout - create an invoice, and attach its id to the payment.source
|
28
|
+
invoice = new_invoice(order, payment)
|
29
|
+
payment.source.invoice_id = invoice['id']
|
30
|
+
payment.source.save!
|
31
|
+
payment.started_processing!
|
32
|
+
else
|
33
|
+
# An invoice was already created - find it
|
34
|
+
invoice = payment.source.find_invoice
|
35
|
+
end
|
36
|
+
|
37
|
+
@invoice_iframe_url = "#{invoice['url']}&view=iframe"
|
38
|
+
render json: @invoice_iframe_url
|
39
|
+
end
|
40
|
+
|
41
|
+
# View Invoice with specific ID
|
42
|
+
#
|
43
|
+
def view_invoice
|
44
|
+
invoice = BitpayInvoice.find(params[:source_id]).find_invoice
|
45
|
+
redirect_to (invoice["url"] + '&view=iframe')
|
46
|
+
end
|
47
|
+
|
48
|
+
def check_payment_state
|
49
|
+
invoice = BitpayInvoice.where(invoice_id: params[:invoice_id]).first
|
50
|
+
pm = PaymentMethod.find(invoice.payment_method_id)
|
51
|
+
status = pm.scan_the_server(invoice.invoice_id)
|
52
|
+
render json: status
|
53
|
+
end
|
54
|
+
|
55
|
+
def cancel
|
56
|
+
|
57
|
+
order = current_order || raise(ActiveRecord::RecordNotFound)
|
58
|
+
|
59
|
+
# Find and invalidate Bitpay payment in processing state
|
60
|
+
order.payments.with_state("processing").each do |payment|
|
61
|
+
if (payment.payment_method.is_a? Spree::PaymentMethod::Bitpay)
|
62
|
+
payment.state = "invalid" # Have to set this explicitly since Spree state machine prevents it
|
63
|
+
payment.save!
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
redirect_to edit_order_url(order, state: 'payment'), :notice => Spree.t(:checkout_cancelled)
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
# Fires on receipt of payment received window message
|
72
|
+
#
|
73
|
+
def payment_sent
|
74
|
+
|
75
|
+
order = Spree::Order.find(session[:order_id]) || raise(ActiveRecord::RecordNotFound)
|
76
|
+
|
77
|
+
session[:order_id] = nil # Reset cart
|
78
|
+
redirect_to spree.order_path(order), :notice => Spree.t(:order_processed_successfully)
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
## Handle IPN from Bitpay server
|
83
|
+
# Receives incoming IPN message and retrieves official BitPay invoice for processing
|
84
|
+
#
|
85
|
+
def notification
|
86
|
+
|
87
|
+
posData = JSON.parse(params["posData"])
|
88
|
+
|
89
|
+
order_id = posData["orderID"]
|
90
|
+
payment_id = posData["paymentID"]
|
91
|
+
|
92
|
+
# Get OFFICIAL Invoice from BitPay API
|
93
|
+
# Fetching payment this way should prevent any false payment/order mismatch
|
94
|
+
order = Spree::Order.find_by_number(order_id) || raise(ActiveRecord::RecordNotFound)
|
95
|
+
payment = order.payments.find_by(identifier: payment_id) || raise(ActiveRecord::RecordNotFound)
|
96
|
+
invoice = payment.source.find_invoice
|
97
|
+
|
98
|
+
if invoice
|
99
|
+
logger.debug("Bitpay Invoice Content: " + invoice.to_json)
|
100
|
+
process_invoice(invoice)
|
101
|
+
head :ok
|
102
|
+
else
|
103
|
+
raise "Spree_Bitpay: No invoice found for notification for #{payment.identifier} from #{request.remote_ip}"
|
104
|
+
end
|
105
|
+
|
106
|
+
rescue
|
107
|
+
logger.error "Spree_Bitpay: Unprocessable notification received from #{request.remote_ip}: #{params.inspect}"
|
108
|
+
head :unprocessable_entity
|
109
|
+
end
|
110
|
+
|
111
|
+
# Reprocess Invoice and update order status
|
112
|
+
#
|
113
|
+
def refresh
|
114
|
+
payment = Spree::Payment.find(params[:payment]) # Retrieve payment by ID
|
115
|
+
old_state = payment.state
|
116
|
+
invoice = payment.source.find_invoice # Get associated invoice
|
117
|
+
process_invoice(invoice) # Re-process invoice
|
118
|
+
new_state = payment.reload.state
|
119
|
+
notice = (new_state == old_state) ? Spree.t(:bitpay_payment_not_updated) : (Spree.t(:bitpay_payment_updated) + new_state.titlecase)
|
120
|
+
redirect_to (request.referrer || root_path), notice: notice
|
121
|
+
end
|
122
|
+
|
123
|
+
#######################################################################
|
124
|
+
### Private Methods
|
125
|
+
#######################################################################
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
# Call Bitpay API and return new JSON invoice object
|
130
|
+
#
|
131
|
+
def new_invoice(order, payment)
|
132
|
+
|
133
|
+
# Have to encode this into a string for proper handling by API
|
134
|
+
posDataJson = {paymentID: payment.identifier, orderID: order.number}.to_json
|
135
|
+
|
136
|
+
invoice_params = {
|
137
|
+
price: order.outstanding_balance,
|
138
|
+
currency: order.currency,
|
139
|
+
orderID: order.number,
|
140
|
+
notificationURL: bitpay_notification_url,
|
141
|
+
posData: posDataJson,
|
142
|
+
fullNotifications: "true"
|
143
|
+
}
|
144
|
+
|
145
|
+
logger.debug "Requesting Invoice with params: #{invoice_params}"
|
146
|
+
invoice = payment.payment_method.get_bitpay_client.post 'invoice', invoice_params
|
147
|
+
logger.debug "Invoice Generated: #{invoice.inspect}"
|
148
|
+
|
149
|
+
return invoice
|
150
|
+
end
|
151
|
+
|
152
|
+
# Process the invoice and adjust order state accordingly
|
153
|
+
# Accepts BitPay JSON invoice object
|
154
|
+
#
|
155
|
+
def process_invoice(invoice)
|
156
|
+
logger.debug "Processing Bitpay invoice"
|
157
|
+
|
158
|
+
# Extract posData
|
159
|
+
posData = JSON.parse(invoice["posData"])
|
160
|
+
|
161
|
+
payment_id = posData["paymentID"]
|
162
|
+
status = invoice["status"]
|
163
|
+
exception_status = invoice["exceptionStatus"]
|
164
|
+
|
165
|
+
payment = Spree::Payment.find_by(identifier: payment_id) || raise(ActiveRecord::RecordNotFound)
|
166
|
+
|
167
|
+
logger.debug "Found Payment: #{payment.inspect}"
|
168
|
+
|
169
|
+
# Advance Payment state according to Spree flow
|
170
|
+
# http://guides.spreecommerce.com/user/payment_states.html
|
171
|
+
|
172
|
+
case status
|
173
|
+
when "new"
|
174
|
+
|
175
|
+
payment.started_processing
|
176
|
+
|
177
|
+
when "paid"
|
178
|
+
|
179
|
+
# Move payment to pending state and complete order
|
180
|
+
|
181
|
+
if payment.state = "processing" # This is the most common scenario
|
182
|
+
payment.pend!
|
183
|
+
else # In the case it was previously marked invalid due to invoice expiry
|
184
|
+
payment.state = "pending"
|
185
|
+
payment.save
|
186
|
+
end
|
187
|
+
|
188
|
+
payment.order.update!
|
189
|
+
payment.order.next
|
190
|
+
if (!payment.order.complete?)
|
191
|
+
raise "Can't transition order #{payment.order.number} to COMPLETE state"
|
192
|
+
end
|
193
|
+
|
194
|
+
when "confirmed", "complete"
|
195
|
+
|
196
|
+
# Move payment to 'complete' state
|
197
|
+
|
198
|
+
case payment.state
|
199
|
+
when "pending" # This is the most common scenario
|
200
|
+
payment.complete
|
201
|
+
when "completed"
|
202
|
+
# Do nothing
|
203
|
+
else
|
204
|
+
# Something unusual happened - maybe a notification was missed, or
|
205
|
+
# Make sure the order is completed
|
206
|
+
if !payment.order.complete?
|
207
|
+
payment.state = "pending"
|
208
|
+
payment.save
|
209
|
+
payment.order.next
|
210
|
+
if (!payment.order.complete?)
|
211
|
+
raise "Can't transition order #{payment.order.number} to COMPLETE state"
|
212
|
+
end
|
213
|
+
end
|
214
|
+
payment.state = "completed" # Can't use Spree payment.complete! method since we are transitioning from weird states
|
215
|
+
payment.save
|
216
|
+
end
|
217
|
+
|
218
|
+
when "expired"
|
219
|
+
|
220
|
+
if (exception_status == false) # This is an abandoned invoice
|
221
|
+
payment.state = "invalid" # Have to set this explicitly since Spree state machine prevents it
|
222
|
+
payment.save!
|
223
|
+
else
|
224
|
+
# Don't think this will be anything other than paidPartial exceptionStatus
|
225
|
+
unless payment.state == 'void'
|
226
|
+
payment.void!
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
when "invalid"
|
231
|
+
|
232
|
+
unless payment.state == 'failed'
|
233
|
+
payment.failure! # Will be flagged risky automatically
|
234
|
+
end
|
235
|
+
|
236
|
+
else
|
237
|
+
|
238
|
+
raise "Unexpected status received from BitPay: '#{invoice["status"]}' for '#{invoice["url"]}"
|
239
|
+
|
240
|
+
end
|
241
|
+
|
242
|
+
logger.debug "New Payment State for #{payment.identifier}: #{payment.state}"
|
243
|
+
logger.debug "New Order State for #{payment.order.number}: #{payment.order.state}"
|
244
|
+
|
245
|
+
end
|
246
|
+
|
247
|
+
end
|
248
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Spree
|
2
|
+
|
3
|
+
class BitpayInvoice < ActiveRecord::Base
|
4
|
+
belongs_to :payment_method
|
5
|
+
belongs_to :user
|
6
|
+
has_many :payments, as: :source
|
7
|
+
|
8
|
+
# DB fields: user_id, invoice_id
|
9
|
+
|
10
|
+
attr_accessor :bogus # bogus since we need to have a param that is passed to trigger Payment.build_source
|
11
|
+
|
12
|
+
def actions
|
13
|
+
# TODO: Refund action?
|
14
|
+
["void"]
|
15
|
+
end
|
16
|
+
|
17
|
+
# Gets the JSON invoice from Bitpay
|
18
|
+
def find_invoice
|
19
|
+
payment_method.find_invoice(invoice_id)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Spree
|
2
|
+
class PaymentMethod::Bitpay < PaymentMethod
|
3
|
+
preference :api_key, :string
|
4
|
+
preference :api_endpoint, :string, :default => "https://bitpay.com/api"
|
5
|
+
|
6
|
+
def auto_capture?
|
7
|
+
true
|
8
|
+
end
|
9
|
+
|
10
|
+
def payment_source_class
|
11
|
+
Spree::BitpayInvoice
|
12
|
+
end
|
13
|
+
|
14
|
+
# Set true to force confirmation step.
|
15
|
+
# http://guides.spreecommerce.com/developer/checkout.html#confirmation
|
16
|
+
#
|
17
|
+
def payment_profiles_supported?
|
18
|
+
true
|
19
|
+
end
|
20
|
+
|
21
|
+
## Dummy method to satisfy test factories
|
22
|
+
#
|
23
|
+
def create_profile(payment)
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def source_required?
|
28
|
+
false
|
29
|
+
end
|
30
|
+
|
31
|
+
#######################################################################
|
32
|
+
### Instance Utility Methods
|
33
|
+
#######################################################################
|
34
|
+
|
35
|
+
## Retreive Invoice by ID from BitPay
|
36
|
+
#
|
37
|
+
def find_invoice(id)
|
38
|
+
id ? ( get_bitpay_client.get ('invoice/' + id) ) : nil
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
def scan_the_server(id)
|
43
|
+
message = get_bitpay_client.get('invoice/' + id)
|
44
|
+
status = message["status"]
|
45
|
+
return status unless status.nil?
|
46
|
+
error = message["error"]["message"] if status.nil?
|
47
|
+
return error unless error.nil?
|
48
|
+
"unknown error"
|
49
|
+
end
|
50
|
+
|
51
|
+
## Interface with BitPay
|
52
|
+
#
|
53
|
+
def get_bitpay_client
|
54
|
+
BitPay::Client.new(preferred_api_key, {api_uri: preferred_api_endpoint})
|
55
|
+
end
|
56
|
+
|
57
|
+
## This is a stub method which simply returns true to allow the cancel on the Spree side
|
58
|
+
#
|
59
|
+
def void (action, order_id, id)
|
60
|
+
ActiveMerchant::Billing::Response.new(true, 'Bogus Gateway: Forced success')
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
<!-- insert_after 'erb[loud]:contains("payment_method")' -->
|
2
|
+
|
3
|
+
<% if (payment.payment_method.is_a? Spree::PaymentMethod::Bitpay) && (payment.state != 'void') %>
|
4
|
+
<% pm = payment.payment_method %>
|
5
|
+
<%= image_tag("BC_nBG_64px.png") %>
|
6
|
+
<script type="text/javascript">
|
7
|
+
$(document).ready(function() {
|
8
|
+
$('[data-hook="buttons"] input[name="commit"]').click(Bitpay.checkout);
|
9
|
+
$('#checkout_form_confirm').removeAttr("action");
|
10
|
+
$('#checkout_form_confirm').removeAttr("method");
|
11
|
+
|
12
|
+
$('#bitpay_checkout_modal').easyModal({
|
13
|
+
overlayClose: false,
|
14
|
+
closeOnEscape: false
|
15
|
+
});
|
16
|
+
|
17
|
+
Bitpay.invoiceUrl = "<%= j bitpay_pay_now_url(pmid: pm.id).html_safe %>";
|
18
|
+
Bitpay.checkUrl = "<%= j bitpay_check_url %>";
|
19
|
+
Bitpay.apiEndpoint = "<%= pm.get_preference(:api_endpoint) %>";
|
20
|
+
})
|
21
|
+
|
22
|
+
window.addEventListener('message', Bitpay.finishCheckout, false);
|
23
|
+
|
24
|
+
</script>
|
25
|
+
|
26
|
+
<div id="bitpay_checkout_modal">
|
27
|
+
<div id="bitpay_modal_content">
|
28
|
+
<div id="bitpay_invoice">
|
29
|
+
<iframe id="bitpay_invoice_iframe"
|
30
|
+
scrolling="no"
|
31
|
+
allowtransparency="true"
|
32
|
+
frameborder="0", >
|
33
|
+
</iframe>
|
34
|
+
</div>
|
35
|
+
<div id="bitpay_checkout_guidance">
|
36
|
+
<span id="instructions"><%= simple_format Spree.t(:bitpay_payment_instructions) %></span>
|
37
|
+
<span id="completed" style="display:none"><%= Spree.t(:bitpay_payment_completed) %></span>
|
38
|
+
<span id="expired" style="display:none"><%= Spree.t(:bitpay_invoice_expired) %></span>
|
39
|
+
</br>
|
40
|
+
</div>
|
41
|
+
<div id="bitpay_payment_buttons" class="form-buttons">
|
42
|
+
<a id="continue_to_invoice"
|
43
|
+
class="button disabled"
|
44
|
+
disabled="disabled"
|
45
|
+
href="<%= bitpay_payment_sent_url %>">
|
46
|
+
<span><%= Spree.t(:bitpay_view_invoice) %></span>
|
47
|
+
</a>
|
48
|
+
<%= link_to Spree.t(:bitpay_cancel), bitpay_cancel_url, {id: "choose_another_method", class: "button primary"} %>
|
49
|
+
</div>
|
50
|
+
</div>
|
51
|
+
</div>
|
52
|
+
|
53
|
+
<% end %>
|
54
|
+
|
55
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<% invoice = @payment.source.find_invoice %>
|
2
|
+
<% if invoice %>
|
3
|
+
<div class="bitpay_invoice_details">
|
4
|
+
<h3>Bitpay Invoice Details</h3>
|
5
|
+
<pre><%= JSON.pretty_generate(invoice) %></pre>
|
6
|
+
</div>
|
7
|
+
|
8
|
+
<%= link_to Spree.t(:bitpay_refresh_invoice), bitpay_refresh_url(payment: @payment), {id: "refresh_invoice", class: "button primary", title: Spree.t(:bitpay_refresh_invoice_description)} %>
|
9
|
+
|
10
|
+
<div class="bitpay_invoice_details">
|
11
|
+
<h3>Invoice</h3>
|
12
|
+
<iframe id="bitpay_view_invoice_iframe"
|
13
|
+
src="<%= j bitpay_view_invoice_url(source_id: @payment.source).html_safe %>"
|
14
|
+
scrolling="no"
|
15
|
+
allowtransparency="true"
|
16
|
+
frameborder="0">
|
17
|
+
</iframe>
|
18
|
+
<br/>
|
19
|
+
<%= link_to Spree.t(:bitpay_view_full_invoice), invoice["url"], {target: "_new"} %>
|
20
|
+
</div>
|
21
|
+
<% else %>
|
22
|
+
<p>No BitPay Invoice Available</p>
|
23
|
+
<% end %>
|
data/bin/rails
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
|
2
|
+
|
3
|
+
ENGINE_ROOT = File.expand_path('../..', __FILE__)
|
4
|
+
ENGINE_PATH = File.expand_path('../../lib/spree_bitpay/engine', __FILE__)
|
5
|
+
|
6
|
+
require 'rails/all'
|
7
|
+
require 'rails/engine/commands'
|
@@ -0,0 +1,16 @@
|
|
1
|
+
en:
|
2
|
+
spree:
|
3
|
+
bitpay_cancel: "Choose another payment method"
|
4
|
+
bitpay_checkout_error: "Error with Bitcoin checkout"
|
5
|
+
bitpay_payment_completed: "Thank you for your payment."
|
6
|
+
bitpay_invoice_expired: "The invoice has expired. Please re-select Bitcoin if you would like to retry."
|
7
|
+
bitpay_payment_instructions: |
|
8
|
+
Please pay the invoice above to continue.
|
9
|
+
The page will automatically refresh in a few seconds when transaction is broadcast.
|
10
|
+
bitpay_payment_method: "Payment will be requested following the confirmation."
|
11
|
+
bitpay_view_invoice: "Continue to Invoice"
|
12
|
+
bitpay_view_full_invoice: "View invoice at BitPay.com"
|
13
|
+
bitpay_refresh_invoice: "Reprocess Invoice"
|
14
|
+
bitpay_refresh_invoice_description: "Re-poll BitPay invoice and update order status"
|
15
|
+
bitpay_payment_updated: "Payment status changed to: "
|
16
|
+
bitpay_payment_not_updated: "No status change detected."
|
data/config/routes.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
Spree::Core::Engine.routes.draw do
|
2
|
+
get '/spree_bitpay/invoice/new', :to => "bitpay#pay_now", :as => :bitpay_pay_now
|
3
|
+
get '/spree_bitpay/invoice/view', :to => "bitpay#view_invoice", :as => :bitpay_view_invoice
|
4
|
+
get '/spree_bitpay/payment_sent', :to => "bitpay#payment_sent", :as => :bitpay_payment_sent
|
5
|
+
get '/spree_bitpay/cancel', :to => "bitpay#cancel", :as => :bitpay_cancel
|
6
|
+
get '/spree_bitpay/refresh', :to => "bitpay#refresh", :as => :bitpay_refresh
|
7
|
+
get '/spree_bitpay/check', :to => "bitpay#check_payment_state", :as => :bitpay_check
|
8
|
+
post '/spree_bitpay/notification', :to => "bitpay#notification", :as => :bitpay_notification
|
9
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class AddFieldsToSpreeBitpayInvoice < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
|
4
|
+
unless Spree::BitpayInvoice.column_names.include? "payment_method_id"
|
5
|
+
add_column :spree_bitpay_invoices, :payment_method_id, :integer
|
6
|
+
add_index :spree_bitpay_invoices, :payment_method_id
|
7
|
+
end
|
8
|
+
|
9
|
+
unless Spree::BitpayInvoice.column_names.include? "user_id"
|
10
|
+
add_column :spree_bitpay_invoices, :user_id, :integer
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module SpreeBitpay
|
2
|
+
module Generators
|
3
|
+
class InstallGenerator < Rails::Generators::Base
|
4
|
+
|
5
|
+
class_option :auto_run_migrations, :type => :boolean, :default => false
|
6
|
+
|
7
|
+
def add_javascripts
|
8
|
+
append_file 'vendor/assets/javascripts/spree/frontend/all.js', "//= require spree/frontend/spree_bitpay\n"
|
9
|
+
append_file 'vendor/assets/javascripts/spree/frontend/all.js', "//= require easyModal.js-master/jquery.easyModal\n"
|
10
|
+
append_file 'vendor/assets/javascripts/spree/backend/all.js', "//= require spree/backend/spree_bitpay\n"
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_stylesheets
|
14
|
+
inject_into_file 'vendor/assets/stylesheets/spree/frontend/all.css', " *= require spree/frontend/spree_bitpay\n", :before => /\*\//, :verbose => true
|
15
|
+
inject_into_file 'vendor/assets/stylesheets/spree/backend/all.css', " *= require spree/backend/spree_bitpay\n", :before => /\*\//, :verbose => true
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_migrations
|
19
|
+
run 'bundle exec rake railties:install:migrations FROM=spree_bitpay'
|
20
|
+
end
|
21
|
+
|
22
|
+
def run_migrations
|
23
|
+
run_migrations = options[:auto_run_migrations] || ['', 'y', 'Y'].include?(ask 'Would you like to run the migrations now? [Y/n]')
|
24
|
+
if run_migrations
|
25
|
+
run 'bundle exec rake db:migrate'
|
26
|
+
else
|
27
|
+
puts 'Skipping rake db:migrate, don\'t forget to run it!'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'bitpay'
|
2
|
+
|
3
|
+
module SpreeBitpay
|
4
|
+
class Engine < Rails::Engine
|
5
|
+
require 'spree/core'
|
6
|
+
isolate_namespace Spree
|
7
|
+
engine_name 'spree_bitpay'
|
8
|
+
|
9
|
+
# use rspec for tests
|
10
|
+
config.generators do |g|
|
11
|
+
g.test_framework :rspec
|
12
|
+
end
|
13
|
+
|
14
|
+
initializer "spree.bitpay.payment_methods", :after => "spree.register.payment_methods" do |app|
|
15
|
+
app.config.spree.payment_methods << Spree::PaymentMethod::Bitpay
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.activate
|
19
|
+
Dir.glob(File.join(File.dirname(__FILE__), '../../app/**/*_decorator*.rb')) do |c|
|
20
|
+
Rails.configuration.cache_classes ? require(c) : load(c)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
config.to_prepare &method(:activate).to_proc
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
Spree::PermittedAttributes.source_attributes.push(:bogus)
|
@@ -0,0 +1,6 @@
|
|
1
|
+
FactoryGirl.define do
|
2
|
+
# Define your Spree extensions Factories within this file to enable applications, and other extensions to use and override them.
|
3
|
+
#
|
4
|
+
# Example adding this to your spec_helper will load these Factories for use:
|
5
|
+
# require 'spree_bitpay/factories'
|
6
|
+
end
|
data/lib/spree_bitpay.rb
ADDED
data/script/rails
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
|
2
|
+
|
3
|
+
ENGINE_ROOT = File.expand_path('../..', __FILE__)
|
4
|
+
ENGINE_PATH = File.expand_path('../../lib/spree_bitpay/engine', __FILE__)
|
5
|
+
|
6
|
+
require 'rails/all'
|
7
|
+
require 'rails/engine/commands'
|
@@ -0,0 +1,54 @@
|
|
1
|
+
## Factories for setting up Spree BitPay inflight orders/payments
|
2
|
+
#
|
3
|
+
#
|
4
|
+
|
5
|
+
# Easily recognizable dummy values
|
6
|
+
|
7
|
+
PAYMENT_ID = "123PAYMENTID"
|
8
|
+
ORDER_ID = "123ORDERID"
|
9
|
+
INVOICE_ID = "123BitPayInvoiceID"
|
10
|
+
|
11
|
+
|
12
|
+
FactoryGirl.define do
|
13
|
+
## Creates a payment in 'processing' state with associated order in 'confirm' state
|
14
|
+
#
|
15
|
+
factory :abstract_btc_payment, class: Spree::Payment do
|
16
|
+
association :payment_method, factory: :bitcoin_payment_method
|
17
|
+
association :source, factory: :bitcoin_invoice
|
18
|
+
amount { order.total }
|
19
|
+
response_code 'BTC'
|
20
|
+
after(:create) do |payment|
|
21
|
+
payment.identifier = PAYMENT_ID
|
22
|
+
payment.save!
|
23
|
+
payment.order.update!
|
24
|
+
end
|
25
|
+
|
26
|
+
factory :processing_payment_with_confirming_order do
|
27
|
+
state 'processing'
|
28
|
+
association :order, factory: :order_with_line_items, state: "confirm", number: ORDER_ID
|
29
|
+
end
|
30
|
+
|
31
|
+
factory :pending_payment_with_complete_order do
|
32
|
+
state 'pending'
|
33
|
+
association :order, factory: :order_with_line_items, state: "complete", number: ORDER_ID
|
34
|
+
end
|
35
|
+
|
36
|
+
factory :invalid_payment_with_confirming_order do
|
37
|
+
state 'invalid'
|
38
|
+
association :order, factory: :order_with_line_items, state: "confirm", number: ORDER_ID
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
factory :bitcoin_payment_method, class: Spree::PaymentMethod::Bitpay do
|
44
|
+
name 'Bitcoin Auto'
|
45
|
+
environment 'test'
|
46
|
+
end
|
47
|
+
|
48
|
+
factory :bitcoin_invoice, class: Spree::BitpayInvoice do
|
49
|
+
association :user
|
50
|
+
invoice_id INVOICE_ID
|
51
|
+
association :payment_method, factory: :bitcoin_payment_method
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|