Processing Stripe Payments with a Background Worker in Rails
Kỹ thuật được sữ dụng trong bài viết này có thể được sữ dụng để xữ lý bất kỳ công việc dài hạn nào trong background . Ví dụ dưới đây sẽ không thực sự xử lý việc thanh toán mà background job sẽ nhận dữ liệu để xử lý. Tạo một class background job để xử lý thanh toán: rails g job ...
Kỹ thuật được sữ dụng trong bài viết này có thể được sữ dụng để xữ lý bất kỳ công việc dài hạn nào trong background. Ví dụ dưới đây sẽ không thực sự xử lý việc thanh toán mà background job sẽ nhận dữ liệu để xử lý.
Tạo một class background job để xử lý thanh toán:
rails g job stripe_payment
Lúc này file app/jobs/stripe_payment_job.rb sẽ như sau:
class StripePaymentJob < ApplicationJob queue_as :default def perform(*args) # Xử lý thanh toán ở đây end end
Thêm gem rescue vào Gemfile và chạy bundle:
gem 'resque' # Chạy bundle bundle install
Trong file application.rb định nghĩa queue_adapter:
config.active_job.queue_adapter = :resque
Tạo một controller để xử lý thanh toán.
rails g controller payments create
Nó sẽ nhận một token và payment id trong action create và sẽ xử lý thanh toán trong background:
class PaymentsController < ApplicationController def create StripePaymentJob.perform_later('process payment later') end end
Nó sẽ chỉ gửi một chuỗi tham số, bạn có thể gửi toklen và payment id dưới dạng hash cho phương thức perform_later. Hãy khởi động server và vào địa chỉ http://localhost:3000/payments/create. Bạn sẽ thấy:
Started GET "/payments/create" for ::1 at 2016-06-07 19:39:45 -0700 Processing by PaymentsController#create as HTML [ActiveJob] Enqueued StripePaymentJob (Job ID: 3b3ab29c-5978-4f70-912f-bf5ec0e8a98e) to Resque(default) with arguments: "process payment later"
Server sẽ lấy thông tin mỗi 5s. AJAX sẽ gọi payments/show để kiểm tra trạng thái của thanh toán bằng function poll trong tệp tin log:
(function poll(){ setTimeout(function(){ $.ajax({ url: "show", success: function(data){ //Setup the next poll recursively poll(); }, dataType: "json"}); }, 5000); })();
Thêm phương thức show vào PaymentsController:
def show render json: { result: 'success'} end
Bây giờ chúng ta có thể tự quản lý trạng thái của thanh toán (success, failure, pending) trong file payment.js:
(function poll(){ setTimeout(function(){ $.ajax({ url: "show", success: function(data){ console.log(data.result); if(data.result === 'success') { alert('It was success'); } else if (data.result === 'failure') { alert('It failed'); } else if (data.result === 'pending') { //Setup the next poll recursively poll(); } }, dataType: "json"}); }, 5000); })();
Chúng ta tiếp tục check cho trường hợp thanh toán đang ở trạng thái pending bằng cách mô phỏng trường hợp fail trong action show:
def show render json: { result: 'failure'} end
Chúng ta có thể thêm trạng thái của thanh toán để báo cho người dùng biết trạng thái hiện tại của payment.
(function poll(){ setTimeout(function(){ $.ajax({ url: "show", beforeSend: function() { $('#loader').show(); }, success: function(data){ console.log(data.result); if(data.result === 'success') { $('#loader').hide(); $('#status').html('Payment processed successfully.') } else if (data.result === 'failure') { $('#loader').hide(); $('#status').html('Payment processing failed.') } else if (data.result === 'pending') { //Setup the next poll recursively poll(); } }, dataType: "json"}); }, 5000); })();
Thêm đoạn code sau vào PaymentsController:
def create @payment = OpenStruct.new(id: 200) StripePaymentJob.perform_later('process payment later') end
Chúng ta có thể sử dụng javascript window object để thiếp lập paymentID trong create.html.erb:
<h1>Payments#create</h1> <%= javascript_tag do %> window.paymentID = '<%= @payment.id %>'; <% end %> <div id='loader'> <img src="http://preloaders.net/preloaders/712/Floating%20rays.gif"/> </div> <div id='status'>Payment is being processed. Please wait...</div>
Sau đó, chuyển action show sang fail:
def show logger.info params render json: { result: 'failure'} end
Lúc này, chúng ta có thể truyền paymentID từ create.html.erb sang payment.js
(function poll(){ setTimeout(function(){ $.ajax({ url: "show/" + paymentID, beforeSend: function() { $('#loader').show(); console.log(paymentID); }, success: function(data){ console.log(data.result); if(data.result === 'success') { $('#loader').hide(); $('#status').html('Payment processed successfully.') } else if (data.result === 'failure') { $('#loader').hide(); $('#status').html('Payment processing failed.') } else if (data.result === 'pending') { //Setup the next poll recursively poll(); } }, dataType: "json"}); }, 5000); })();
Nếu bạn sẽ gặp phải lỗi sau:
GET http://localhost:3000/payments/show/200 404 (Not Found)
thì hãy định nghĩa routes:
get 'payments/show/:id', to: 'payments#show'
Bây giờ chúng ta có thể thấy được payment id đã được truyền lên server
Started GET "/payments/show/200" for ::1 at 2016-06-07 15:52:20 -0700 Processing by PaymentsController#show as JSON Parameters: {"id"=>"200"}
Chúng ta có thể sử dụng payment id để kiểm tra trạng thái của xử lý thanh toán bằng cách đẩy vào trong database. Hãy hiển thị receipt id trong trường hợp thanh toán thành công:
$('#status').html('Payment processed successfully. Your receipt ID is : ' + data.receipt_id);
Thêm đoạn code sau vào PaymentsController:
def show logger.info params render json: { result: 'success', receipt_id: 'CXM4873'} end
Sử dụng các thuộc tính dữ liệu của HTML 5 để loại bỏ các window object mà chúng ta đã thêm vào ở phần trên:
<div id='loader' data-url="<%= payments_show_url(@payment.id) %>"> <img src="http://preloaders.net/preloaders/712/Floating%20rays.gif"/> </div>
Thêm vào thư mục routes sau để hiển thị:
get 'payments/show/:id', to: 'payments#show', as: :payments_show
Xóa phần truyền paymentID từ rails qua javascript đã thêm ở trên:
<h1>Payments#create</h1> <div id='loader' data-url="<%= payments_show_url(@payment.id) %>"> <img src="http://preloaders.net/preloaders/712/Floating%20rays.gif"/> </div> <div id='status'>Payment is being processed. Please wait...</div>
Cuối cùng file payment.js sẽ như sau:
(function poll(){ setTimeout(function(){ $.ajax({ url: $('#loader').data('url'), beforeSend: function() { $('#loader').show(); }, success: function(data){ console.log(data.result); if(data.result === 'success') { $('#loader').hide(); $('#status').html('Payment processed successfully. Your receipt ID is : ' + data.receipt_id); } else if (data.result === 'failure') { $('#loader').hide(); $('#status').html('Payment processing failed.') } else if (data.result === 'pending') { //Setup the next poll recursively poll(); } }, dataType: "json"}); }, 5000); })();
https://rubyplus.com/articles/4111-Processing-Stripe-Payments-with-a-Background-Worker-in-Rails-5