spree_bitpay 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.
- 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
|