liqpay 0.1.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: cb8c24034981cbb7c7ce37aac9f8bb24746b8e2f
4
+ data.tar.gz: 4a65b5598669d54294f98bd40dadd0acfda7d6ec
5
+ SHA512:
6
+ metadata.gz: 44978d20a3d2e76a3e8af6c143a9f2b7cf12ef7a615cbb131fead7a56ad2f84747c9714d496161e32c6659f909b22cffaafcb9046b70758c173a95d22dd12f27
7
+ data.tar.gz: 1830483461086edb9144f5ecbaca1205e1746bd35879bb00ff7d19c8f97784a61eba06e7d0d9e8080964f128c89267e3d0927acc3c70575aad928ffc27fb4f81
@@ -1,3 +1,7 @@
1
+ ## 1.0.0
2
+
3
+ * Updated gem to be compatible with the new LiqPAY API
4
+
1
5
  ## 0.1.2
2
6
 
3
7
  * Fixed initializer syntax
data/README.md CHANGED
@@ -1,47 +1,62 @@
1
1
  # LiqPAY
2
2
 
3
- This Ruby gem implements the [LiqPAY](http://liqpay.com) billing system.
3
+ This Ruby gem implements the [LiqPAY](https://www.liqpay.com) billing system API, as described in [the LiqPAY documentation](https://www.liqpay.com/doc).
4
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.
5
+ **Users of version 0.1.2 and earlier:** your version of the gem uses the older, deprecated LiqPAY API; you should migrate to v1.0.0, but it requires you to change configuration and set up a server callback endpoint, so it's not a trivial upgrade.
6
+
7
+ ## Demo
8
+
9
+ There is a demo app at http://liqpay-demo.herokuapp.com, source at https://github.com/leonid-shevtsov/liqpay_demo
6
10
 
7
11
  ## Installation
8
12
 
9
- Include the [liqpay gem](https://rubygems.org/gems/liqpay) in your app.
13
+ Include the [liqpay gem](https://rubygems.org/gems/liqpay) in your `Gemfile`:
14
+
15
+ ```ruby
16
+ gem 'liqpay', '~>1.0.0'
17
+ ```
18
+
19
+ The gem requries at least Ruby 1.9.
10
20
 
11
21
  ## Configuration
12
22
 
13
- The recommended way is setting the `Liqpay.default_options` hash somewhere in
23
+ 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
14
24
  your initializers.
15
25
 
16
- You MUST supply the `merchant_id` and `merchant_signature` options, that are
17
- provided by LiqPAY when you sign up, on the [merchant details page](https://liqpay.com/?do=shop_access).
26
+ You should supply the `public_key` and `private_key` options, that are
27
+ provided by LiqPAY when you sign up and create a shop on the [shops page](https://www.liqpay.com/admin/business):
18
28
 
19
- ## Processing payments through Liq&Buy
29
+ ```ruby
30
+ # config/initializers/liqpay.rb
31
+ Liqpay.default_options = {
32
+ public_key: ENV['LIQPAY_PUBLIC_KEY'],
33
+ private_key: ENV['LIQPAY_PRIVATE_KEY'],
34
+ currency: 'UAH'
35
+ }
36
+ ```
37
+
38
+
39
+ ## Processing payments through LiqPay
20
40
 
21
41
  ### General flow
22
42
 
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
43
+ 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.
24
44
 
25
45
  2. Users completes payment through LiqPAY.
26
46
 
27
- 3. LiqPAY redirects the user to the URL you specified.
47
+ 3. LiqPAY redirects the user to the URL you specified with GET.
28
48
 
29
- 4. You validate the response against your secret signature.
49
+ 4. You wait for a callback that LiqPAY will POST to your designated `server_url`.
30
50
 
31
51
  5. If the payment was successful: You process the payment on your side.
32
52
 
33
53
  6. If the payment was cancelled: You cancel the operation.
34
54
 
35
- So, LiqPAY is pretty simple, it does no server-to-server validation, just a
36
- browser-driven flow.
55
+ 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.
37
56
 
38
57
  ### Implementation in Rails
39
58
 
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'
59
+ 0. Configure Liqpay
45
60
 
46
61
  1. Create a `Liqpay::Request` object
47
62
 
@@ -53,61 +68,70 @@ browser-driven flow.
53
68
  model (I suggest that you should), pass its ID. If not, it can be a random
54
69
  string stored in the session, or whatever, but *it must be unique*.
55
70
 
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
- )
71
+ ```ruby
72
+ @liqpay_request = Liqpay::Request.new(
73
+ amount: '999.99',
74
+ currency: 'UAH',
75
+ order_id: '123',
76
+ description: 'Some Product',
77
+ result_url: order_url(@order),
78
+ server_url: liqpay_payment_url
79
+ )
80
+ ```
63
81
 
64
82
  **Note that this does not do anything permanent.** No saves to the database, no
65
83
  requests to LiqPAY.
66
84
 
67
-
68
-
69
85
  2. Put a payment button somewhere
70
86
 
71
87
  As you need to make a POST request, there is definitely going to be a form somewhere.
72
88
 
73
89
  To output a form consisting of a single "Pay with LiqPAY" button, do
74
90
 
75
- <%=liqpay_button @liqpay_request %>
91
+ ```erb
92
+ <%=liqpay_button @liqpay_request %>
93
+ ```
76
94
 
77
95
  Or:
78
96
 
79
- <%=liqpay_button @liqpay_request, :title => "Pay now!" %>
97
+ ```erb
98
+ <%=liqpay_button @liqpay_request, title: "Pay now!" %>
99
+ ```
80
100
 
81
101
  Or:
82
102
 
83
- <%=liqpay_button @liqpay_request do %>
84
- <%=link_to 'Pay now!', '#', :onclick => 'document.forms[0].submit();' %>
85
- <% end %>
103
+ ```erb
104
+ <%=liqpay_button @liqpay_request do %>
105
+ <%=link_to 'Pay now!', '#', onclick: 'document.forms[0].submit();' %>
106
+ <% end %>
107
+ ```
86
108
 
87
109
  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
+
111
+ ```ruby
112
+ # config/routes.rb
113
+ post '/liqpay_payment' => 'payments#liqpay_payment'
114
+
115
+ # app/controllers/payments_controller.rb
116
+ class PaymentsController < ApplicationController
117
+ # Skipping forgery protection here is important
118
+ protect_from_forgery :except => :liqpay_payment
119
+
120
+ def liqpay_payment
121
+ @liqpay_response = Liqpay::Response.new(params)
122
+
123
+ if @liqpay_response.success?
124
+ # check that order_id is valid
125
+ # check that amount matches
126
+ # handle success
127
+ else
128
+ # handle error
110
129
  end
130
+ rescue Liqpay::InvalidResponse
131
+ # handle error
132
+ end
133
+ end
134
+ ```
111
135
 
112
136
  That's about it.
113
137
 
@@ -119,4 +143,4 @@ That's about it.
119
143
 
120
144
  - - -
121
145
 
122
- 2012 Leonid Shevtsov
146
+ Ruby implementation (c) 2012-2014 Leonid Shevtsov
@@ -5,9 +5,8 @@ require 'liqpay/response'
5
5
  require 'liqpay/railtie' if defined?(Rails)
6
6
 
7
7
  module Liqpay
8
- LIQBUY_API_VERSION = '1.2'
9
- LIQBUY_ENDPOINT_URL = 'https://www.liqpay.com/?do=clickNbuy'
10
- SUPPORTED_CURRENCIES = %w(UAH USD EUR RUR)
8
+ LIQPAY_ENDPOINT_URL = 'https://www.liqpay.com/api/pay'
9
+ SUPPORTED_CURRENCIES = %w(UAH USD EUR RUB)
11
10
 
12
11
  @default_options = {}
13
12
  class << self; attr_accessor :default_options; end
@@ -3,22 +3,26 @@ require 'base64'
3
3
 
4
4
  module Liqpay
5
5
  class BaseOperation
6
- attr_accessor :merchant_id, :merchant_signature
6
+ attr_accessor :public_key, :private_key
7
7
 
8
8
  def initialize(options={})
9
9
  options.replace(Liqpay.default_options.merge(options))
10
10
 
11
- @merchant_id = options[:merchant_id]
12
- @merchant_signature = options[:merchant_signature]
11
+ @public_key = options[:public_key]
12
+ @private_key = options[:private_key]
13
13
  end
14
14
 
15
15
  def signature
16
- @signature ||= sign(xml, @merchant_signature)
16
+ @signature ||= sign(signature_fields)
17
+ end
18
+
19
+ def signature_fields
20
+ raise NotImplementedError
17
21
  end
18
22
 
19
23
  private
20
- def sign(xml, signature)
21
- Base64.encode64(Digest::SHA1.digest(signature + xml + signature)).strip
24
+ def sign(fields)
25
+ Base64.encode64(Digest::SHA1.digest(@private_key + fields.join(''))).strip
22
26
  end
23
27
  end
24
28
  end
@@ -12,8 +12,11 @@ module Liqpay
12
12
  def liqpay_button(liqpay_request, options={}, &block)
13
13
  id = options.fetch(:id, 'liqpay_form')
14
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)
15
+ content_tag(:form, :id => id, :action => Liqpay::LIQPAY_ENDPOINT_URL, :method => :post) do
16
+ result = liqpay_request.form_fields.map{|name, value|
17
+ hidden_field_tag(name, value)
18
+ }.join("\n").html_safe
19
+
17
20
  if block_given?
18
21
  result += yield
19
22
  else
@@ -7,77 +7,56 @@ module Liqpay
7
7
  attr_accessor :amount
8
8
  # REQUIRED Currency of payment - one of `Liqpay::SUPPORTED_CURRENCIES`
9
9
  attr_accessor :currency
10
- # REQUIRED Arbitrary but unique ID
10
+ # REQUIRED Description to be displayed to the user
11
+ attr_accessor :description
12
+ # RECOMMENDED Arbitrary but unique ID (May be REQUIRED by LiqPay configuration)
11
13
  attr_accessor :order_id
12
14
  # RECOMMENDED URL that the user will be redirected to after payment
13
15
  attr_accessor :result_url
14
16
  # RECOMMENDED URL that'll receive the order details in the background.
15
17
  attr_accessor :server_url
16
- # RECOMMENDED Description to be displayed to the user
17
- attr_accessor :description
18
- # Phone number to be suggested to the user
19
- #
20
- # LiqPAY requires users to provide a phone number before payment.
21
- # If you know the user's phone number, you can provide it so he
22
- # doesn't have to enter it manually.
23
- attr_accessor :default_phone
24
- # Method of payment. One or more (comma-separated) of:
25
- # card - by card
26
- # liqpay - by liqpay account
27
- attr_accessor :pay_way
28
-
29
- attr_accessor :exp_time
30
- attr_accessor :goods_id
18
+ # OPTIONAL type of payment = either `buy` (the default) or `donate`
19
+ attr_accessor :type
20
+ # OPTIONAL UI language - `ru` or `en`
21
+ attr_accessor :language
31
22
 
32
23
  def initialize(options={})
33
24
  super(options)
34
-
35
- @result_url = options[:result_url]
36
- @server_url = options[:server_url]
37
- @order_id = options[:order_id]
25
+
38
26
  @amount = options[:amount]
39
27
  @currency = options[:currency]
40
28
  @description = options[:description]
41
- @default_phone = options[:default_phone]
42
- @pay_way = options[:pay_way]
29
+ @order_id = options[:order_id]
30
+ @result_url = options[:result_url]
31
+ @server_url = options[:server_url]
32
+ @type = options[:type]
33
+ @language = options[:language]
43
34
  @kamikaze = options[:kamikaze]
44
35
  end
45
36
 
46
- def encoded_xml
47
- @encoded_xml ||= encode(xml)
48
- end
49
-
50
- def xml
51
- @xml ||= make_xml
37
+ def signature_fields
38
+ [amount, currency, public_key, order_id, type, description, result_url, server_url]
52
39
  end
53
40
 
54
-
55
- private
56
- def encode(xml)
57
- Base64.encode64(xml).gsub(/\s/,'')
58
- end
59
-
60
- def make_xml
41
+ def form_fields
61
42
  validate! unless @kamikaze
62
- Nokogiri::XML::Builder.new { |xml|
63
- xml.request {
64
- xml.version Liqpay::LIQBUY_API_VERSION
65
- xml.merchant_id merchant_id
66
- xml.result_url result_url
67
- xml.server_url server_url
68
- xml.order_id order_id
69
- xml.amount "%0.2f" % amount
70
- xml.currency currency
71
- xml.description description
72
- xml.default_phone default_phone
73
- xml.pay_way pay_way.is_a?(Array) ? pay_way.join(',') : pay_way
74
- }
75
- }.to_xml
43
+ {
44
+ public_key: public_key,
45
+ amount: amount,
46
+ currency: currency,
47
+ description: description,
48
+ order_id: order_id,
49
+ result_url: result_url,
50
+ server_url: server_url,
51
+ type: type,
52
+ signature: signature,
53
+ language: language
54
+ }.reject{|k,v| v.nil?}
76
55
  end
77
56
 
57
+ private
78
58
  def validate!
79
-
80
- %w(merchant_id merchant_signature currency amount order_id).each do |required_field|
59
+ %w(public_key amount currency description).each do |required_field|
81
60
  raise Liqpay::Exception.new(required_field + ' is a required field') unless self.send(required_field).to_s != ''
82
61
  end
83
62
 
@@ -89,7 +68,7 @@ module Liqpay
89
68
  raise Liqpay::Exception.new('amount must be a number')
90
69
  end
91
70
 
92
- raise Liqpay::Exception.new('goods_id must only contain digits') unless goods_id.to_s =~ /\A\d*\Z/
71
+ raise Liqpay::Exception.new('amount must be rounded to 2 decimal digits') unless self.amount.round(2) == self.amount
93
72
 
94
73
  raise Liqpay::Exception.new('amount must be more than 0.01') unless amount > 0.01
95
74
  end
@@ -1,15 +1,12 @@
1
1
  require 'base64'
2
- require 'nokogiri'
3
2
  require 'liqpay/base_operation'
4
3
 
5
4
  module Liqpay
6
5
  class Response < BaseOperation
7
6
  SUCCESS_STATUSES = %w(success wait_secure)
8
7
 
9
- attr_reader :encoded_xml, :signature, :xml
10
-
11
- ATTRIBUTES = %w(merchant_id order_id amount currency description status code transaction_id pay_way sender_phone goods_id pays_count)
12
- %w(merchant_id order_id description goods_id pays_count).each do |attr|
8
+ ATTRIBUTES = %w(public_key order_id amount currency description type status transaction_id sender_phone)
9
+ %w(public_key order_id description type).each do |attr|
13
10
  attr_reader attr
14
11
  end
15
12
 
@@ -22,20 +19,18 @@ module Liqpay
22
19
  # success
23
20
  # wait_secure - success, but the card wasn't known to the system
24
21
  attr_reader :status
25
- # Error code
26
- attr_reader :code
27
22
  # LiqPAY's internal transaction ID
28
23
  attr_reader :transaction_id
29
- # Chosen method of payment
30
- attr_reader :pay_way
31
24
  # Payer's phone
32
25
  attr_reader :sender_phone
33
26
 
34
- def initialize(options = {})
27
+ def initialize(params = {}, options = {})
35
28
  super(options)
36
29
 
37
- @encoded_xml = options[:operation_xml]
38
- @signature = options[:signature]
30
+ ATTRIBUTES.each do |attribute|
31
+ instance_variable_set "@#{attribute}", params[attribute]
32
+ end
33
+ @request_signature = params["signature"]
39
34
 
40
35
  decode!
41
36
  end
@@ -45,19 +40,15 @@ module Liqpay
45
40
  SUCCESS_STATUSES.include? self.status
46
41
  end
47
42
 
43
+ def signature_fields
44
+ [amount, currency, public_key, order_id, type, description, status, transaction_id, sender_phone]
45
+ end
46
+
48
47
  private
49
48
  def decode!
50
- @xml = Base64.decode64(@encoded_xml)
51
-
52
- if sign(@xml, @merchant_signature) != @signature
49
+ if signature != @request_signature
53
50
  raise Liqpay::InvalidResponse
54
51
  end
55
-
56
- doc = Nokogiri.XML(@xml)
57
-
58
- ATTRIBUTES.each do |attr|
59
- self.instance_variable_set('@'+attr, doc.at(attr).try(:content))
60
- end
61
52
  end
62
53
  end
63
54
  end
@@ -1,3 +1,3 @@
1
1
  module Liqpay
2
- VERSION = "0.1.2"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -11,7 +11,6 @@ Gem::Specification.new do |s|
11
11
  s.summary = %q{LiqPAY billing API implementation in Ruby}
12
12
  s.description = %q{LiqPAY billing API implementation in Ruby}
13
13
 
14
- s.add_dependency 'nokogiri'
15
14
  s.add_development_dependency 'rake'
16
15
 
17
16
  s.files = `git ls-files`.split("\n")
metadata CHANGED
@@ -1,38 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: liqpay
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
5
- prerelease:
4
+ version: 1.0.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Leonid Shevtsov
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-02-24 00:00:00.000000000 Z
11
+ date: 2014-03-14 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
- name: nokogiri
16
- requirement: &70239324845220 !ruby/object:Gem::Requirement
17
- none: false
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
18
16
  requirements:
19
- - - ! '>='
17
+ - - '>='
20
18
  - !ruby/object:Gem::Version
21
19
  version: '0'
22
- type: :runtime
20
+ type: :development
23
21
  prerelease: false
24
- version_requirements: *70239324845220
25
- - !ruby/object:Gem::Dependency
26
- name: rake
27
- requirement: &70239324844620 !ruby/object:Gem::Requirement
28
- none: false
22
+ version_requirements: !ruby/object:Gem::Requirement
29
23
  requirements:
30
- - - ! '>='
24
+ - - '>='
31
25
  - !ruby/object:Gem::Version
32
26
  version: '0'
33
- type: :development
34
- prerelease: false
35
- version_requirements: *70239324844620
36
27
  description: LiqPAY billing API implementation in Ruby
37
28
  email:
38
29
  - leonid@shevtsov.me
@@ -55,32 +46,25 @@ files:
55
46
  - liqpay.gemspec
56
47
  homepage: https://github.com/leonid-shevtsov/liqpay
57
48
  licenses: []
49
+ metadata: {}
58
50
  post_install_message:
59
51
  rdoc_options: []
60
52
  require_paths:
61
53
  - lib
62
54
  required_ruby_version: !ruby/object:Gem::Requirement
63
- none: false
64
55
  requirements:
65
- - - ! '>='
56
+ - - '>='
66
57
  - !ruby/object:Gem::Version
67
58
  version: '0'
68
- segments:
69
- - 0
70
- hash: 3679480104141870157
71
59
  required_rubygems_version: !ruby/object:Gem::Requirement
72
- none: false
73
60
  requirements:
74
- - - ! '>='
61
+ - - '>='
75
62
  - !ruby/object:Gem::Version
76
63
  version: '0'
77
- segments:
78
- - 0
79
- hash: 3679480104141870157
80
64
  requirements: []
81
65
  rubyforge_project:
82
- rubygems_version: 1.8.11
66
+ rubygems_version: 2.1.11
83
67
  signing_key:
84
- specification_version: 3
68
+ specification_version: 4
85
69
  summary: LiqPAY billing API implementation in Ruby
86
70
  test_files: []