liqpay 0.1.2 → 1.0.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.
@@ -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: []