+1

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

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

1.png

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 2.png

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&notify_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

3.png

Đã đăng nhập

4.png

Thanh toán

5.png

Trả về app của mình

6.png

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!!


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí