liqpay 0.0.1 → 0.1.0
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.
- data/.gitignore +1 -0
- data/README.md +120 -1
- data/lib/liqpay.rb +4 -2
- data/lib/liqpay/liqpay_helper.rb +26 -0
- data/lib/liqpay/railtie.rb +7 -0
- data/lib/liqpay/request.rb +26 -2
- data/lib/liqpay/response.rb +26 -1
- data/lib/liqpay/version.rb +1 -1
- data/liqpay.gemspec +1 -1
- metadata +35 -51
data/README.md
CHANGED
@@ -1,3 +1,122 @@
|
|
1
1
|
# LiqPAY
|
2
2
|
|
3
|
-
This Ruby gem implements the [LiqPAY](http://liqpay.com) billing system
|
3
|
+
This Ruby gem implements the [LiqPAY](http://liqpay.com) billing system.
|
4
|
+
|
5
|
+
As of now it only covers the [Liq&Buy 1.2 API](https://liqpay.com/?do=pages&p=cnb12), which is used to accept credit card payments on your site.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Include the [liqpay gem](https://rubygems.org/gems/liqpay) in your app.
|
10
|
+
|
11
|
+
## Configuration
|
12
|
+
|
13
|
+
The recommended way is setting the `Liqpay.default_options` hash somewhere in
|
14
|
+
your initializers.
|
15
|
+
|
16
|
+
You MUST supply the `merchant_id` and `merchant_signature` options, that are
|
17
|
+
provided by LiqPAY when you sign up.
|
18
|
+
|
19
|
+
## Processing payments through Liq&Buy
|
20
|
+
|
21
|
+
### General flow
|
22
|
+
|
23
|
+
1. User initiates the payment process; you redirect him to LiqPAY via POST, providing necessary parameters to set up the payment's amount and description
|
24
|
+
|
25
|
+
2. Users completes payment through LiqPAY.
|
26
|
+
|
27
|
+
3. LiqPAY redirects the user to the URL you specified.
|
28
|
+
|
29
|
+
4. You validate the response against your secret signature.
|
30
|
+
|
31
|
+
5. If the payment was successful: You process the payment on your side.
|
32
|
+
|
33
|
+
6. If the payment was cancelled: You cancel the operation.
|
34
|
+
|
35
|
+
So, LiqPAY is pretty simple, it does no server-to-server validation, just a
|
36
|
+
browser-driven flow.
|
37
|
+
|
38
|
+
### Implementation in Rails
|
39
|
+
|
40
|
+
0. Configure Liqpay:
|
41
|
+
|
42
|
+
# config/initializers/liqpay.rb
|
43
|
+
Liqpay.default_options[:merchant_id] = 'MY_MERCHANT_ID'
|
44
|
+
Liqpay.default_options[:merchant_signature] = 'MY_MERCHANT_SIGNATURE'
|
45
|
+
|
46
|
+
1. Create a `Liqpay::Request` object
|
47
|
+
|
48
|
+
The required options are: the amount and currency of the payment, and an
|
49
|
+
"order ID".
|
50
|
+
|
51
|
+
The "order ID" is just a random string that you will use to
|
52
|
+
identify the payment after it has been completed. If you have an `Order`
|
53
|
+
model (I suggest that you should), pass its ID. If not, it can be a random
|
54
|
+
string stored in the session, or whatever, but *it must be unique*.
|
55
|
+
|
56
|
+
@liqpay_request = Liqpay::Request.new(
|
57
|
+
:amount => '999.99',
|
58
|
+
:currency => 'UAH',
|
59
|
+
:order_id => '123',
|
60
|
+
:description => 'Some Product',
|
61
|
+
:result_url => liqpay_payment_url
|
62
|
+
)
|
63
|
+
|
64
|
+
**Note that this does not do anything permanent.** No saves to the database, no
|
65
|
+
requests to LiqPAY.
|
66
|
+
|
67
|
+
|
68
|
+
|
69
|
+
2. Put a payment button somewhere
|
70
|
+
|
71
|
+
As you need to make a POST request, there is definitely going to be a form somewhere.
|
72
|
+
|
73
|
+
To output a form consisting of a single "Pay with LiqPAY" button, do
|
74
|
+
|
75
|
+
<%=liqpay_button @liqpay_request %>
|
76
|
+
|
77
|
+
Or:
|
78
|
+
|
79
|
+
<%=liqpay_button @liqpay_request, :title => "Pay now!" %>
|
80
|
+
|
81
|
+
Or:
|
82
|
+
|
83
|
+
<%=liqpay_button @liqpay_request do %>
|
84
|
+
<%=link_to 'Pay now!', '#', :onclick => 'document.forms[0].submit();' %>
|
85
|
+
<% end %>
|
86
|
+
|
87
|
+
3. Set up a receiving endpoint.
|
88
|
+
|
89
|
+
# config/routes.rb
|
90
|
+
post '/liqpay_payment' => 'payments#liqpay_payment'
|
91
|
+
|
92
|
+
# app/controllers/payments_controller.rb
|
93
|
+
class PaymentsController < ApplicationController
|
94
|
+
# Skipping forgery protection here is important
|
95
|
+
protect_from_forgery :except => :liqpay_payment
|
96
|
+
|
97
|
+
def liqpay_payment
|
98
|
+
@liqpay_response = Liqpay::Response.new(params)
|
99
|
+
|
100
|
+
if @liqpay_response.success?
|
101
|
+
# check that order_id is valid
|
102
|
+
# check that amount matches
|
103
|
+
# handle success
|
104
|
+
else
|
105
|
+
# handle error
|
106
|
+
end
|
107
|
+
rescue Liqpay::InvalidResponse
|
108
|
+
# handle error
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
That's about it.
|
113
|
+
|
114
|
+
### Security considerations
|
115
|
+
|
116
|
+
* Check that amount from response matches the expected amount;
|
117
|
+
* check that the order id is valid;
|
118
|
+
* check that the order isn't completed yet (to avoid replay attacks);
|
119
|
+
|
120
|
+
- - -
|
121
|
+
|
122
|
+
2012 Leonid Shevtsov
|
data/lib/liqpay.rb
CHANGED
@@ -2,9 +2,11 @@ require 'liqpay/version'
|
|
2
2
|
require 'liqpay/request'
|
3
3
|
require 'liqpay/response'
|
4
4
|
|
5
|
+
require 'liqpay/railtie' if defined?(Rails)
|
6
|
+
|
5
7
|
module Liqpay
|
6
|
-
|
7
|
-
|
8
|
+
LIQBUY_API_VERSION = '1.2'
|
9
|
+
LIQBUY_ENDPOINT_URL = 'https://www.liqpay.com/?do=clickNbuy'
|
8
10
|
SUPPORTED_CURRENCIES = %w(UAH USD EUR RUR)
|
9
11
|
|
10
12
|
@default_options = {}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Liqpay
|
2
|
+
module LiqpayHelper
|
3
|
+
# Displays a form to send a payment request to LiqPay
|
4
|
+
#
|
5
|
+
# You can either pass in a block, that SHOULD render a submit button (or not, if you plan to submit the form otherwise), or
|
6
|
+
# let the helper create a simple submit button for you.
|
7
|
+
#
|
8
|
+
# liqpay_request - an instance of Liqpay::Request
|
9
|
+
# options - currently accepts two options
|
10
|
+
# id - the ID of the form being created (`liqpay_form` by default)
|
11
|
+
# title - text on the submit button (`Pay with LiqPay` by default); not used if you pass in a block
|
12
|
+
def liqpay_button(liqpay_request, options={}, &block)
|
13
|
+
id = options.fetch(:id, 'liqpay_form')
|
14
|
+
title = options.fetch(:title, 'Pay with LiqPAY')
|
15
|
+
content_tag(:form, :id => id, :action => Liqpay::LIQBUY_ENDPOINT_URL, :method => :post) do
|
16
|
+
result = hidden_field_tag(:operation_xml, liqpay_request.encoded_xml)+hidden_field_tag(:signature, liqpay_request.signature)
|
17
|
+
if block_given?
|
18
|
+
result += yield
|
19
|
+
else
|
20
|
+
result += submit_tag(title, :name => nil)
|
21
|
+
end
|
22
|
+
result
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/liqpay/request.rb
CHANGED
@@ -2,7 +2,31 @@ require 'base64'
|
|
2
2
|
|
3
3
|
module Liqpay
|
4
4
|
class Request < BaseOperation
|
5
|
-
|
5
|
+
# REQUIRED Amount of payment (Float), in :currency
|
6
|
+
attr_accessor :amount
|
7
|
+
# REQUIRED Currency of payment - one of `Liqpay::SUPPORTED_CURRENCIES`
|
8
|
+
attr_accessor :currency
|
9
|
+
# REQUIRED Arbitrary but unique ID
|
10
|
+
attr_accessor :order_id
|
11
|
+
# RECOMMENDED URL that the user will be redirected to after payment
|
12
|
+
attr_accessor :result_url
|
13
|
+
# RECOMMENDED URL that'll receive the order details in the background.
|
14
|
+
attr_accessor :server_url
|
15
|
+
# RECOMMENDED Description to be displayed to the user
|
16
|
+
attr_accessor :description
|
17
|
+
# Phone number to be suggested to the user
|
18
|
+
#
|
19
|
+
# LiqPAY requires users to provide a phone number before payment.
|
20
|
+
# If you know the user's phone number, you can provide it so he
|
21
|
+
# doesn't have to enter it manually.
|
22
|
+
attr_accessor :default_phone
|
23
|
+
# Method of payment. One or more (comma-separated) of:
|
24
|
+
# card - by card
|
25
|
+
# liqpay - by liqpay account
|
26
|
+
attr_accessor :pay_way
|
27
|
+
|
28
|
+
attr_accessor :exp_time
|
29
|
+
attr_accessor :goods_id
|
6
30
|
|
7
31
|
def initialize(options={})
|
8
32
|
super(options)
|
@@ -36,7 +60,7 @@ module Liqpay
|
|
36
60
|
validate! unless @kamikaze
|
37
61
|
Nokogiri::XML::Builder.new { |xml|
|
38
62
|
xml.request {
|
39
|
-
xml.version Liqpay::
|
63
|
+
xml.version Liqpay::LIQBUY_API_VERSION
|
40
64
|
xml.merchant_id merchant_id
|
41
65
|
xml.result_url result_url
|
42
66
|
xml.server_url server_url
|
data/lib/liqpay/response.rb
CHANGED
@@ -3,13 +3,33 @@ require 'nokogiri'
|
|
3
3
|
|
4
4
|
module Liqpay
|
5
5
|
class Response < BaseOperation
|
6
|
+
SUCCESS_STATUSES = %w(success wait_secure)
|
7
|
+
|
6
8
|
attr_reader :encoded_xml, :signature, :xml
|
7
9
|
|
8
10
|
ATTRIBUTES = %w(merchant_id order_id amount currency description status code transaction_id pay_way sender_phone goods_id pays_count)
|
9
|
-
|
11
|
+
%w(merchant_id order_id amount currency description status code transaction_id pay_way sender_phone goods_id pays_count).each do |attr|
|
10
12
|
attr_reader attr
|
11
13
|
end
|
12
14
|
|
15
|
+
# Amount of payment. MUST match the requested amount
|
16
|
+
attr_reader :amount
|
17
|
+
# Currency of payment. MUST match the requested currency
|
18
|
+
attr_reader :currency
|
19
|
+
# Status of payment. One of '
|
20
|
+
# failure
|
21
|
+
# success
|
22
|
+
# wait_secure - success, but the card wasn't known to the system
|
23
|
+
attr_reader :status
|
24
|
+
# Error code
|
25
|
+
attr_reader :code
|
26
|
+
# LiqPAY's internal transaction ID
|
27
|
+
attr_reader :transaction_id
|
28
|
+
# Chosen method of payment
|
29
|
+
attr_reader :pay_way
|
30
|
+
# Payer's phone
|
31
|
+
attr_reader :sender_phone
|
32
|
+
|
13
33
|
def initialize(options = {})
|
14
34
|
super(options)
|
15
35
|
|
@@ -19,6 +39,11 @@ module Liqpay
|
|
19
39
|
decode!
|
20
40
|
end
|
21
41
|
|
42
|
+
# Returns true, if the transaction was successful
|
43
|
+
def success?
|
44
|
+
SUCCESS_STATUSES.include? self.status
|
45
|
+
end
|
46
|
+
|
22
47
|
private
|
23
48
|
def decode!
|
24
49
|
@xml = Base64.decode64(@encoded_xml)
|
data/lib/liqpay/version.rb
CHANGED
data/liqpay.gemspec
CHANGED
@@ -7,7 +7,7 @@ Gem::Specification.new do |s|
|
|
7
7
|
s.version = Liqpay::VERSION
|
8
8
|
s.authors = ["Leonid Shevtsov"]
|
9
9
|
s.email = ["leonid@shevtsov.me"]
|
10
|
-
s.homepage = "
|
10
|
+
s.homepage = "https://github.com/leonid-shevtsov/liqpay"
|
11
11
|
s.summary = %q{LiqPAY billing API implementation in Ruby}
|
12
12
|
s.description = %q{LiqPAY billing API implementation in Ruby}
|
13
13
|
|
metadata
CHANGED
@@ -1,90 +1,74 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: liqpay
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 0
|
9
|
-
- 1
|
10
|
-
version: 0.0.1
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Leonid Shevtsov
|
14
9
|
autorequire:
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
dependencies:
|
21
|
-
- !ruby/object:Gem::Dependency
|
12
|
+
date: 2012-01-27 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
22
15
|
name: nokogiri
|
23
|
-
|
24
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
16
|
+
requirement: &70327090826000 !ruby/object:Gem::Requirement
|
25
17
|
none: false
|
26
|
-
requirements:
|
27
|
-
- -
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
|
30
|
-
segments:
|
31
|
-
- 0
|
32
|
-
version: "0"
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
33
22
|
type: :runtime
|
34
|
-
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70327090826000
|
35
25
|
description: LiqPAY billing API implementation in Ruby
|
36
|
-
email:
|
26
|
+
email:
|
37
27
|
- leonid@shevtsov.me
|
38
28
|
executables: []
|
39
|
-
|
40
29
|
extensions: []
|
41
|
-
|
42
30
|
extra_rdoc_files: []
|
43
|
-
|
44
|
-
files:
|
31
|
+
files:
|
45
32
|
- .gitignore
|
46
33
|
- Gemfile
|
47
34
|
- README.md
|
48
35
|
- Rakefile
|
49
36
|
- lib/liqpay.rb
|
50
37
|
- lib/liqpay/base_operation.rb
|
38
|
+
- lib/liqpay/liqpay_helper.rb
|
39
|
+
- lib/liqpay/railtie.rb
|
51
40
|
- lib/liqpay/request.rb
|
52
41
|
- lib/liqpay/response.rb
|
53
42
|
- lib/liqpay/version.rb
|
54
43
|
- liqpay.gemspec
|
55
|
-
|
56
|
-
homepage: http://leonid.shevtsov.me/en/liqpay
|
44
|
+
homepage: https://github.com/leonid-shevtsov/liqpay
|
57
45
|
licenses: []
|
58
|
-
|
59
46
|
post_install_message:
|
60
47
|
rdoc_options: []
|
61
|
-
|
62
|
-
require_paths:
|
48
|
+
require_paths:
|
63
49
|
- lib
|
64
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
51
|
none: false
|
66
|
-
requirements:
|
67
|
-
- -
|
68
|
-
- !ruby/object:Gem::Version
|
69
|
-
|
70
|
-
segments:
|
52
|
+
requirements:
|
53
|
+
- - ! '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
segments:
|
71
57
|
- 0
|
72
|
-
|
73
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
hash: 3173392958928029467
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
60
|
none: false
|
75
|
-
requirements:
|
76
|
-
- -
|
77
|
-
- !ruby/object:Gem::Version
|
78
|
-
|
79
|
-
segments:
|
61
|
+
requirements:
|
62
|
+
- - ! '>='
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: '0'
|
65
|
+
segments:
|
80
66
|
- 0
|
81
|
-
|
67
|
+
hash: 3173392958928029467
|
82
68
|
requirements: []
|
83
|
-
|
84
69
|
rubyforge_project:
|
85
|
-
rubygems_version: 1.
|
70
|
+
rubygems_version: 1.8.11
|
86
71
|
signing_key:
|
87
72
|
specification_version: 3
|
88
73
|
summary: LiqPAY billing API implementation in Ruby
|
89
74
|
test_files: []
|
90
|
-
|