Skip to content

leonid-shevtsov/liqpay

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

27 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

LiqPAY

Buy me a coffee Пригостіть кавою, якщо цей гем для вас є корисним.

This Ruby gem implements the LiqPAY billing system API, as described in the LiqPAY documentation.

Demo

There is a demo app source at https://github.com/leonid-shevtsov/liqpay_demo. Heroku removed free app hosting, so no more online demo. Also, Liqpay eventually blocks such usage of keys, so it won't survive for long.

Installation

Include the liqpay gem in your Gemfile:

gem 'liqpay', '~>2.0.0'

The gem requires at least Ruby 2.7.

Configuration

You can provide all of the payment options in the request object, but the recommended way is setting the Liqpay.default_options hash somewhere in your initializers.

You should supply the public_key and private_key options, that are provided by LiqPAY when you sign up and create a shop on the shops page:

# config/initializers/liqpay.rb
Liqpay.default_options = {
    public_key: ENV['LIQPAY_PUBLIC_KEY'],
    private_key: ENV['LIQPAY_PRIVATE_KEY'],
    currency: 'UAH'
}

Processing payments through LiqPay

General flow

  1. User initiates the payment process; you redirect him to LiqPAY via a POST form, providing necessary parameters such as the payment's amount, order id and description.

  2. Users completes payment through LiqPAY.

  3. LiqPAY redirects the user to the URL you specified with GET.

  4. You wait for a callback that LiqPAY will POST to your designated server_url.

  5. If the payment was successful: You process the payment on your side.

  6. If the payment was cancelled: You cancel the operation.

The most recent version of the LiqPAY API requires you to have a serverside endpoint, which makes it impossible to test it with a local address. Use ngrok for local testing.

Implementation in Rails

  1. Configure Liqpay

  2. Create a Liqpay::Request object

    The required options are: the amount and currency of the payment, and an "order ID".

    The "order ID" is just a random string that you will use to identify the payment after it has been completed. If you have an Order model (I suggest that you should), pass its ID. If not, it can be a random string stored in the session, or whatever, but it must be unique.

    @liqpay_request = Liqpay::Request.new(
      amount: '999.99',
      currency: 'UAH',
      order_id: '123',
      description: 'Some Product',
      result_url: order_url(@order),
      server_url: liqpay_payment_url
    )

    Note that this does not do anything permanent. No saves to the database, no requests to LiqPAY.

  3. Put a payment button somewhere

    As you need to make a POST request, there is definitely going to be a form somewhere.

    To output a form consisting of a single "Pay with LiqPAY" button, do

    <%=liqpay_button @liqpay_request %>

    Or:

    <%=liqpay_button @liqpay_request, title: "Pay now!" %>

    Or:

    <%=liqpay_button @liqpay_request do %>
      <%=link_to 'Pay now!', '#', onclick: 'document.forms[0].submit();' %>
    <% end %>
  4. Set up a receiving endpoint.

    # config/routes.rb
    post '/liqpay_payment' => 'payments#liqpay_payment'
    
    # app/controllers/payments_controller.rb
    class PaymentsController < ApplicationController
      # Skipping forgery protection here is important
      protect_from_forgery :except => :liqpay_payment
    
      def liqpay_payment
        @liqpay_response = Liqpay::Response.new(params)
    
        if @liqpay_response.success?
          # check that order_id is valid
          # check that amount matches
          # handle success
        else
          # handle error
        end
      rescue Liqpay::InvalidResponse
        # handle error
      end
    end

That's about it.

Security considerations

  • Check that amount from response matches the expected amount;
  • check that the order id is valid;
  • check that the order isn't completed yet (to avoid replay attacks);

Ruby implementation (c) 2012-2022 Leonid Shevtsov