Xây dựng thanh toán trực tuyến sử dụng bên thứ 3 - PayPal
Xu hướng sử dụng phương thức thanh toán trực tuyến không còn xa lạ với khá nhiều người, nó đang trở thành xu thế vì thói lười ra khỏi nhà. Vậy để xây dựng 1 phương thức thanh toán như thế trong website của chính mình thì ntn. Mình xin hướng dẫn các bạn sử dụng thanh toán qua 1 bên thứ 3 PAYPAL ...
Xu hướng sử dụng phương thức thanh toán trực tuyến không còn xa lạ với khá nhiều người, nó đang trở thành xu thế vì thói lười ra khỏi nhà. Vậy để xây dựng 1 phương thức thanh toán như thế trong website của chính mình thì ntn. Mình xin hướng dẫn các bạn sử dụng thanh toán qua 1 bên thứ 3 PAYPAL
Trước hết bạn phải tạo 1 tài khoản tại đây:
- Chúng ta tạo thêm 1 vài tài khoản để test, k giới hạn số lượng
Giờ thì bem thôi Mình sẽ tạo ra 1 model payment để lưu lại cũng như gửi value để thanh toán
Trong controller payments
def new @payment = Payment.new end def create @payment = Payment.new payment_params if @payment.save redirect_to @payment.paypal_url(payment_path(@payment)) else render :new end end
Models payments
def paypal_url return_path values = { business: ENV["facilitator_paypal"], return: "#{Rails.application.secrets.app_host}#{return_path}", notify_url: "#{Rails.application.secrets.app_host}/update", invoice: id, cmd: "_xclick", amount: amount, } "#{Rails.application.secrets.paypal_host}/cgi-bin/webscr?" + values.to_query end
Chúng ta có thẻ quản lý các parameter từ file config/secrets.yml
development: paypal_host: https://www.sandbox.paypal.com app_host: http://our_ngrok_url production: paypal_host: https://www.paypal.com app_host:
Mình sẽ tạo mới 1 payments khi người dùng thanh toán
Giải thích 1 chút:
-
paypal_url sẽ tương ứng cho đường dẫn thanh toán
-
business: email của tài khoản nhận thanh toán trên paypal do trang web bạn quản lý.
-
cmd: lệnh giao dịch, 1 sản phầm là "_xclick", nếu giao dịch 1 lúc nhiều sản phẩn thì là "_xcartart"
-
return: link đường dẫn sau khi người dùng thanh toán thành công, thường là 1 trang web hiển thị trạng thái thanh toán ở web người bán
-
invoice: id của bản ghi registration để hệ thống cập nhật sau khi Paypal callback
-
amount: khoản tiền cần thanh toán
-
notify_url: link mà paypal sẽ callback lại server bạn sau khi xác minh 1 giao dịch thành công (thường là địa chỉ link trang web của bạn) Lưu ý các địa chỉ nên được lưu vào trong setting
Tiếp đó mình sẽ viết hàm xử lý việc thông tin được gửi trả lại website của chính mình thông qua paypal
... def show @payment = Payment.find_by id: params[:id] if @payment.nil? redirect_to root_path end end def update params.permit! status = params[:payment_status] if status == "Completed" @payment = Payment.find params[:invoice] @payment.update_attributes status: status, transaction_id: params[:txn_id], purchased_at: Time.now end end
Ở đây mình sẽ xử lý khi params được paypal trả về, mình sẽ return ra trang show payments và đồng thời sẽ được update trong database, mình cần thêm 1 vài thứ trong controllers payment để giúp việc đó được thực hiện hiệu quả
#controllers payments ... before_action :update, only: :show protect_from_forgery except: [:update]
Giải thích chút về việc phải sự dụng protect_from_forgery
Các bạn có thể nhận thấy hàm protect_from_forgery được khai báo ngay trong Application_controllers trong mỗi app của rails, nó sẽ kiểm tra xem authenticity_token của được trả về có giống nhau k, nhằm mục đích kiểm tra quyền chứng thực của người dùng - CSRF.
Các bạn có thể thấy logic của việc thanh toán qua paypal khi gửi dữ liệu từ paypal return về web của mình đã chắc chắn sẽ vi phạm vào CSRF nên khi giao dịch thành công, paypal trả về sẽ đương nhiên gặp lỗi, chính vì thế chúng ta cần except protect_from_forgery cho hàm update.
Test thử thôi. Đầu tiên là tạo 1 payment thanh toán mới
Sau khi submit from, app sẽ được redirect_to theo link "https://www.sandbox.paypal.com/cgi-bin/webscr?amount=96&business=vuhuutuan262-buyer-1%40gmail.com&cmd=_xclick&invoice=4¬ify_url=http%3A%2F%2Flocalhost%3A3000%2Fupdate&return=http%3A%2F%2Flocalhost%3A3000%2Fpayments%2F4"
(đây là giá trị lấy được thông qua hàm paypal_url)
Chưa đăng nhập
Đã đăng nhập
Thanh toán
Trả về app của mình
Dữ liệu được trả về app gồm
parameters: !ruby/hash:ActiveSupport::HashWithIndifferentAccess mc_gross: '96.00' invoice: '8' protection_eligibility: Eligible address_status: confirmed payer_id: 68TYR598PYH3U tax: '0.00' address_street: 1 Main St payment_date: 22:56:45 Nov 26, 2016 PST payment_status: Completed charset: windows-1252 address_zip: '95131' first_name: test mc_fee: '3.08' address_country_code: US address_name: test facilitator's Test Store notify_version: '3.8' custom: ' payer_status: verified business: vuhuutuan262-buyer-1@gmail.com address_country: United States address_city: San Jose quantity: '1' payer_email: vuhuutuan262-facilitator@gmail.com verify_sign: AFcWxV21C7fd0v3bYYYRCpSSRl31ADolIEn2MVG3v7UrPgfJ2yPyX8F0 txn_id: 6G165125E3453420G payment_type: instant payer_business_name: test facilitator's Test Store last_name: facilitator address_state: CA receiver_email: vuhuutuan262-buyer-1@gmail.com payment_fee: '3.08' receiver_id: FP6NY45MZGERA txn_type: web_accept item_name: ' mc_currency: USD item_number: ' residence_country: US test_ipn: '1' handling_amount: '0.00' transaction_subject: ' payment_gross: '96.00' shipping: '0.00' auth: AZ9ZrcSDQyqqJwDhbez20ZlJ4382mcsm1oHVQenuGImD89KuUSj93bXeynn7o7OlzJ7sf-yRLsfTTpI3XLhMmqA controller: payments action: show id: '8'
Chúng ta sẽ chọn lọc dữ liệu mình cần để lửu vào database, như trên thì mình sẽ lưu lại
- status: Chỉ khi vào giao dịch thành công thì paypal mới trả về Complete, các bạn nên chú ý điều đó
- transaction_id: id của giao dịch, với mỗi giao dịch đều sinh ra 1 transaction_id nên bạn có thể vào account của mình và tra lại được giao dịch đó, rất là tiện
- purchased_at: mình cần lưu lại thời gian của việc thanh toán trong database của mình cho dễ quản lý
Như vâỵ là đã xây dựng thành công thanh toán qua bên thứ 3, và còn 1 chút chú ý cho các bạn có thể tùy chỉnh với website của mình:
- Với những khách hàng muốn thanh toán nhiều sản phẩm một lúc
#model payment line_items.each_with_index do |item, index| values.merge!({ "amount_#{index + 1}" => item.unit_price, "item_name_#{index + 1}" => item.name, "item_number_#{index + 1}" => item.identifier, "quantity_#{index + 1}" => item.quantity }) end
- Lưu ý bạn nếu trong trường hợp người dùng thanh toán thành công nhưng k return lại website thì payments đấy sẽ k được cập nhật trong database của mình, khi đó bạn có thể update status của payment ngat khi người dùng submit form payment để có thể tiện việc check lại giao dịch.
Hy vọng bài viết sẽ giúp ích được các bạn!!