khipu-rails 0.0.1 → 1.3.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.
@@ -1,33 +1,41 @@
1
1
  module KhipuRails
2
- class Config
3
- def self.khipu_images
4
- {
2
+ class Config < Struct.new(:receivers, :button_images, :button_defaults)
3
+
4
+ def self.default
5
+ config = new
6
+
7
+ config.receivers = []
8
+
9
+ config.button_images = {
5
10
  "50x25" => "https://s3.amazonaws.com/static.khipu.com/buttons/50x25.png",
6
11
  "100x25" => "https://s3.amazonaws.com/static.khipu.com/buttons/100x25.png",
7
12
  "100x50" => "https://s3.amazonaws.com/static.khipu.com/buttons/100x50.png",
8
13
  "150x25" => "https://s3.amazonaws.com/static.khipu.com/buttons/150x25.png",
9
14
  "150x50" => "https://s3.amazonaws.com/static.khipu.com/buttons/150x50.png",
10
15
  "150x75" => "https://s3.amazonaws.com/static.khipu.com/buttons/150x75.png",
11
- "150x75-B" => "https://s3.amazonaws.com/static.khipu.com/buttons/150x75.png",
16
+ "150x75-B" => "https://s3.amazonaws.com/static.khipu.com/buttons/150x75-B.png",
12
17
  "200x50" => "https://s3.amazonaws.com/static.khipu.com/buttons/200x50.png",
13
18
  "200x75" => "https://s3.amazonaws.com/static.khipu.com/buttons/200x75.png"
14
19
  }
15
- end
16
20
 
17
- def self.user_id= value
18
- @@user_id = value
19
- end
20
-
21
- def self.api_key= value
22
- @@api_key = value
23
- end
21
+ config.button_defaults = {
22
+ subject: '',
23
+ amount: 1,
24
+ body: '',
25
+ return_url: '',
26
+ cancel_url: '',
27
+ transaction_id: '',
28
+ payer_email: '',
29
+ picture_url: '',
30
+ custom: '',
31
+ button_image: '50x25'
32
+ }
24
33
 
25
- def self.user_id
26
- @@user_id
34
+ config
27
35
  end
28
36
 
29
- def self.api_key
30
- @@api_key
37
+ def add_receiver id, key, mode = :prod
38
+ self.receivers << KhipuRails::Receiver.new(id, key, mode)
31
39
  end
32
40
  end
33
- end
41
+ end
@@ -0,0 +1,47 @@
1
+ require "httpclient"
2
+ require "openssl"
3
+ require "base64"
4
+
5
+ module KhipuRails
6
+ class NotificationValidator
7
+ def self.is_valid? notification, mode = :local
8
+ data = {
9
+ "api_version" => notification[:api_version],
10
+ "receiver_id" => notification[:receiver_id],
11
+ "notification_id" => notification[:notification_id],
12
+ "subject" => notification[:subject],
13
+ "amount" => notification[:amount],
14
+ "currency" => notification[:currency],
15
+ "transaction_id" => notification[:transaction_id],
16
+ "payer_email" => notification[:payer_email],
17
+ "custom" => notification[:custom]
18
+ }
19
+
20
+ signature = { "notification_signature" => notification[:notification_signature] }
21
+
22
+ self.send mode, data, signature
23
+ end
24
+
25
+ def self.local data, signature
26
+ receiver = KhipuRails.config.receivers.find{|r| r.id == data["receiver_id"]}
27
+ if receiver.present?
28
+ to_validate = data.map{|key, val| key + "=" + val}.join("&")
29
+ signature = Base64.decode64(signature["notification_signature"])
30
+ digest = OpenSSL::Digest::SHA1.new
31
+
32
+ receiver.pkey.verify digest, signature, to_validate
33
+ else
34
+ # Raise exception
35
+ false
36
+ end
37
+ end
38
+
39
+ def self.webservice data, signature
40
+ url = 'https://khipu.com/api/1.1/verifyPaymentNotification'
41
+ to_send = data.merge signature
42
+ client = HTTPClient.new
43
+ response = client.post url, to_send
44
+ response.body == "VERIFIED"
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,17 @@
1
+ require "openssl"
2
+
3
+ module KhipuRails
4
+ class Receiver < Struct.new(:id, :key, :mode)
5
+ def pkey
6
+ @pkey ||= load_key
7
+ end
8
+
9
+ private
10
+
11
+ def load_key
12
+ cert_path = [KhipuRails.root, 'config', mode.eql?(:dev) ? 'khipu_dev.pem.cer' : 'khipu.pem.cer'].join('/')
13
+ cert = OpenSSL::X509::Certificate.new File.read cert_path
14
+ cert.public_key
15
+ end
16
+ end
17
+ end
@@ -1,3 +1,3 @@
1
1
  module KhipuRails
2
- VERSION = "0.0.1"
2
+ VERSION = "1.3.0"
3
3
  end
@@ -0,0 +1,200 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe KhipuRails::ButtonHelper do
5
+ before :all do
6
+ KhipuRails.config = nil
7
+ KhipuRails.configure do |config|
8
+ config.add_receiver "1392", 'b174c94de0ec3ce4f1c3156e309de45e8ce0f9ef', :dev
9
+ end
10
+ @view = ActionView::Base.new
11
+ end
12
+
13
+ it "generates a form with the action provided by khipu" do
14
+ button = Nokogiri::HTML.parse(@view.khipu_button "minimal", 2000)
15
+ form = button.css('form')
16
+ form.attribute('action').value.should == 'https://khipu.com/api/1.3/createPaymentPage'
17
+ end
18
+
19
+ it "generates a form with the method post" do
20
+ button = Nokogiri::HTML.parse(@view.khipu_button "minimal", 2000)
21
+ form = button.css('form')
22
+ form.attribute('method').value.should == 'post'
23
+ end
24
+
25
+ context "given a form with minimal data was generated, it" do
26
+ before :all do
27
+ @receiver_id = KhipuRails.config.receivers.first.id
28
+ @secret = KhipuRails.config.receivers.first.key
29
+ @button = Nokogiri::HTML.parse(@view.khipu_button "minimal", 2000)
30
+ end
31
+
32
+ it "has an input called reciever_id" do
33
+ attribute_value(:receiver_id).should == @receiver_id
34
+ end
35
+
36
+ it "has an input called subject" do
37
+ attribute_value(:subject).should == 'minimal'
38
+ end
39
+
40
+ it "has an input called body" do
41
+ attribute_value(:body).should == ''
42
+ end
43
+
44
+ it "has an input called amount" do
45
+ attribute_value(:amount).should == '2000'
46
+ end
47
+
48
+ it "has an input called return_url" do
49
+ attribute_value(:return_url).should == ''
50
+ end
51
+
52
+ it "has an input called cancel_url" do
53
+ attribute_value(:cancel_url).should == ''
54
+ end
55
+
56
+ it "has an input called transaction_id" do
57
+ attribute_value(:transaction_id).should == ''
58
+ end
59
+
60
+ it "has an input called custom" do
61
+ attribute_value(:custom).should == ''
62
+ end
63
+
64
+ it "has an input called payer_email" do
65
+ attribute_value(:payer_email).should == ''
66
+ end
67
+
68
+ it "has an input called picture_url" do
69
+ attribute_value(:picture_url).should == ''
70
+ end
71
+
72
+ it "has an input called hash" do
73
+ attribute_value(:hash, :type).should == 'hidden'
74
+
75
+ raw = "receiver_id=#{attribute_value :receiver_id}&"
76
+ raw += "subject=#{attribute_value :subject}&"
77
+ raw += "body=#{attribute_value :body}&"
78
+ raw += "amount=#{attribute_value :amount}&"
79
+ raw += "payer_email=#{attribute_value :payer_email}&"
80
+ raw += "bank_id=#{attribute_value :bank_id}&"
81
+ raw += "expires_date=#{attribute_value :expires_date}&"
82
+ raw += "transaction_id=#{attribute_value :transaction_id}&"
83
+ raw += "custom=#{attribute_value :custom}&"
84
+ raw += "notify_url=#{attribute_value :notify_url}&"
85
+ raw += "return_url=#{attribute_value :return_url}&"
86
+ raw += "cancel_url=#{attribute_value :cancel_url}&"
87
+ raw += "picture_url=#{attribute_value :picture_url}"
88
+ attribute_value(:hash).should == OpenSSL::HMAC.hexdigest('sha256', @secret, raw)
89
+ end
90
+
91
+ it "has an image submit" do
92
+ attribute_value(:submit, :type).should == 'image'
93
+ attribute_value(:submit, :src).should == 'https://s3.amazonaws.com/static.khipu.com/buttons/50x25.png'
94
+ end
95
+ end
96
+
97
+ context "given a form with full optional data was generated, it" do
98
+ before :all do
99
+ @receiver_id = 4321
100
+ @secret = 'lkjhgfdsa0987654321'
101
+ @button = Nokogiri::HTML.parse(@view.khipu_button "full", 1_000_000,
102
+ body: 'This is a full body',
103
+ return_url: 'http://foo.bar',
104
+ cancel_url: 'http://bar.foo',
105
+ transaction_id: 'asdf1234',
106
+ payer_email: 'testing@khipu.com',
107
+ picture_url: 'http://upload.wikimedia.org/wikipedia/commons/thumb/e/ec/Mona_Lisa%2C_by_Leonardo_da_Vinci%2C_from_C2RMF_retouched.jpg/402px-Mona_Lisa%2C_by_Leonardo_da_Vinci%2C_from_C2RMF_retouched.jpg',
108
+ custom: "Just testing\nwhat this api\tsupports.",
109
+ button_image: '150x75-B',
110
+ receiver_id: @receiver_id,
111
+ secret: @secret
112
+ )
113
+ end
114
+
115
+ it "has an input called reciever_id" do
116
+ attribute_value(:receiver_id).should == @receiver_id.to_s
117
+ end
118
+
119
+ it "has an input called subject" do
120
+ attribute_value(:subject).should == 'full'
121
+ end
122
+
123
+ it "has an input called body" do
124
+ attribute_value(:body).should == 'This is a full body'
125
+ end
126
+
127
+ it "has an input called amount" do
128
+ attribute_value(:amount).should == '1000000'
129
+ end
130
+
131
+ it "has an input called return_url" do
132
+ attribute_value(:return_url).should == 'http://foo.bar'
133
+ end
134
+
135
+ it "has an input called cancel_url" do
136
+ attribute_value(:cancel_url).should == 'http://bar.foo'
137
+ end
138
+
139
+ it "has an input called transaction_id" do
140
+ attribute_value(:transaction_id).should == 'asdf1234'
141
+ end
142
+
143
+ it "has an input called custom" do
144
+ attribute_value(:custom).should == "Just testing\nwhat this api\tsupports."
145
+ end
146
+
147
+ it "has an input called payer_email" do
148
+ attribute_value(:payer_email).should == 'testing@khipu.com'
149
+ end
150
+
151
+ it "has an input called picture_url" do
152
+ attribute_value(:picture_url).should == 'http://upload.wikimedia.org/wikipedia/commons/thumb/e/ec/Mona_Lisa%2C_by_Leonardo_da_Vinci%2C_from_C2RMF_retouched.jpg/402px-Mona_Lisa%2C_by_Leonardo_da_Vinci%2C_from_C2RMF_retouched.jpg'
153
+ end
154
+
155
+ it "has an input called hash" do
156
+ attribute_value(:hash, :type).should == 'hidden'
157
+
158
+ raw = "receiver_id=#{attribute_value :receiver_id}&"
159
+ raw += "subject=#{attribute_value :subject}&"
160
+ raw += "body=#{attribute_value :body}&"
161
+ raw += "amount=#{attribute_value :amount}&"
162
+ raw += "payer_email=#{attribute_value :payer_email}&"
163
+ raw += "bank_id=#{attribute_value :bank_id}&"
164
+ raw += "expires_date=#{attribute_value :expires_date}&"
165
+ raw += "transaction_id=#{attribute_value :transaction_id}&"
166
+ raw += "custom=#{attribute_value :custom}&"
167
+ raw += "notify_url=#{attribute_value :notify_url}&"
168
+ raw += "return_url=#{attribute_value :return_url}&"
169
+ raw += "cancel_url=#{attribute_value :cancel_url}&"
170
+ raw += "picture_url=#{attribute_value :picture_url}"
171
+ attribute_value(:hash).should == OpenSSL::HMAC.hexdigest('sha256', @secret, raw)
172
+ end
173
+
174
+ it "has an image submit" do
175
+ attribute_value(:submit, :type).should == 'image'
176
+ attribute_value(:submit, :src).should == 'https://s3.amazonaws.com/static.khipu.com/buttons/150x75-B.png'
177
+ end
178
+ end
179
+
180
+ context "form with only options" do
181
+ before :all do
182
+ KhipuRails.config = nil
183
+ KhipuRails.configure do |config|
184
+ config.add_receiver "1392", 'b174c94de0ec3ce4f1c3156e309de45e8ce0f9ef', :dev
185
+ config.add_receiver "321", 'lkjhgfdsa0987654321', :dev
186
+ config.button_defaults.merge! subject: 'Compra de Puntos Cumplo',
187
+ amount: 3000
188
+ end
189
+ end
190
+
191
+ it "allows developers to provide their own button_image" do
192
+ button = Nokogiri::HTML.parse(@view.khipu_button "image", 1)
193
+ input = button.css('form input[name=receiver_id]')
194
+ end
195
+ end
196
+ end
197
+
198
+ def attribute_value input, attribute = :value, form = @button
199
+ form.css("form input[name=#{input.to_s}]").attribute(attribute.to_s).value
200
+ end
@@ -0,0 +1,65 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe KhipuRails::Config do
5
+ context "Empty Configuration" do
6
+ before :each do
7
+ KhipuRails.config = nil
8
+ end
9
+
10
+ it 'has no receiver' do
11
+ KhipuRails.config.receivers.count.should == 0
12
+ end
13
+
14
+ it 'has button_image key/value pairs provided by Khipu' do
15
+ KhipuRails.config.button_images["50x25"].should == "https://s3.amazonaws.com/static.khipu.com/buttons/50x25.png"
16
+ KhipuRails.config.button_images["100x25"].should == "https://s3.amazonaws.com/static.khipu.com/buttons/100x25.png"
17
+ KhipuRails.config.button_images["100x50"].should == "https://s3.amazonaws.com/static.khipu.com/buttons/100x50.png"
18
+ KhipuRails.config.button_images["150x25"].should == "https://s3.amazonaws.com/static.khipu.com/buttons/150x25.png"
19
+ KhipuRails.config.button_images["150x50"].should == "https://s3.amazonaws.com/static.khipu.com/buttons/150x50.png"
20
+ KhipuRails.config.button_images["150x75"].should == "https://s3.amazonaws.com/static.khipu.com/buttons/150x75.png"
21
+ KhipuRails.config.button_images["150x75-B"].should == "https://s3.amazonaws.com/static.khipu.com/buttons/150x75-B.png"
22
+ KhipuRails.config.button_images["200x50"].should == "https://s3.amazonaws.com/static.khipu.com/buttons/200x50.png"
23
+ KhipuRails.config.button_images["200x75"].should == "https://s3.amazonaws.com/static.khipu.com/buttons/200x75.png"
24
+ KhipuRails.config.button_images.count.should == 9
25
+ end
26
+
27
+ it 'sets defaults' do
28
+ KhipuRails.config.button_defaults[:subject].should == ''
29
+ end
30
+ end
31
+ context "Full Configuration" do
32
+ before :all do
33
+ KhipuRails.config = nil
34
+ KhipuRails.configure do |config|
35
+ config.add_receiver '123', '1234567890asdfghjkl', :dev
36
+ config.add_receiver '321', 'lkjhgfdsa0987654321', :dev
37
+ config.button_images.merge! my_button: 'http://my_site.cl/my_button.png'
38
+ config.button_defaults.merge! subject: 'Compra de Puntos Cumplo'
39
+ end
40
+ end
41
+
42
+ it 'adds receiver key/value pairs' do
43
+ KhipuRails.config.receivers.first.id.should == '123'
44
+ KhipuRails.config.receivers.first.key.should == '1234567890asdfghjkl'
45
+ KhipuRails.config.receivers.last.id.should == '321'
46
+ KhipuRails.config.receivers.last.key.should == 'lkjhgfdsa0987654321'
47
+ KhipuRails.config.receivers.find{|r| r.id == '123'}.key.should == '1234567890asdfghjkl'
48
+ KhipuRails.config.receivers.find{|r| r.id == '321'}.key.should == 'lkjhgfdsa0987654321'
49
+ end
50
+
51
+ it 'adds button_image key/value pairs' do
52
+ KhipuRails.config.button_images.keys.last.should == :my_button
53
+ KhipuRails.config.button_images.values.last.should == 'http://my_site.cl/my_button.png'
54
+ KhipuRails.config.button_images[:my_button].should == 'http://my_site.cl/my_button.png'
55
+ end
56
+
57
+ it 'should not delete previous button_image key/value pairs' do
58
+ KhipuRails.config.button_images.count.should_not == 1
59
+ end
60
+
61
+ it 'sets defaults' do
62
+ KhipuRails.config.button_defaults[:subject].should == 'Compra de Puntos Cumplo'
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,64 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe KhipuRails::NotificationValidator do
5
+ context "Valid Notification" do
6
+ before :all do
7
+ @notification ||= {
8
+ api_version: "1.2",
9
+ receiver_id: "1392",
10
+ notification_id: "dfs40ivmw7fz",
11
+ subject: "Compra de Puntos Cumplo",
12
+ amount: "2182",
13
+ currency: "CLP",
14
+ transaction_id: "",
15
+ payer_email: "klahott@gmail.com",
16
+ custom: "",
17
+ notification_signature: "RiJM7qaRjgSBWfsGG7h0P1MHEo3spzXRGN+0BtyGQuZvLgwC6etXHqxxZ6y2r+i0IX5ugTeJhrOg+X2a2mm/MwDlmxoQDeq/Y824/U+zCNfsSDfO8O7+DZgkL2qoZuzMUlWb+DYzbYiVteIpc20ZZIE0PalK6lCr2zt8HM4K76rV5/7UVJvSAbHIDUeArSee/NLR9pq9Cy4t2oob0G4dm4uspEg6mYEtpbP4z85xAz5fZ1XesEycrRpKXblem6ciCEuUFgMGVGnFVxTUI1pkFfOopkoblJ93TdRwUMnHFOOsh37Gwsvk7Y/7W0r3+6wiFd6YJiPa79HzYEpUDSmyIw=="
18
+ }
19
+
20
+ KhipuRails.config = nil
21
+ KhipuRails.configure do |config|
22
+ config.add_receiver "1392", 'b174c94de0ec3ce4f1c3156e309de45e8ce0f9ef', :dev
23
+ end
24
+ end
25
+
26
+ it "validates locally a valid transaction" do
27
+ KhipuRails::NotificationValidator.is_valid?(@notification, :local).should == true
28
+ end
29
+
30
+ it "validates a valid transaction via webserver" do
31
+ KhipuRails::NotificationValidator.is_valid?(@notification, :webservice).should == true
32
+ end
33
+ end
34
+
35
+ context "Invalid Notification" do
36
+ before :all do
37
+ @notification ||= {
38
+ api_version: "1.2",
39
+ receiver_id: "1392",
40
+ notification_id: "dfs40ivmw7fz",
41
+ subject: "",
42
+ amount: "2182",
43
+ currency: "CLP",
44
+ transaction_id: "",
45
+ payer_email: "klahott@gmail.com",
46
+ custom: "",
47
+ notification_signature: "RiJM7qaRjgSBWfsGG7h0P1MHEo3spzXRGN+0BtyGQuZvLgwC6etXHqxxZ6y2r+i0IX5ugTeJhrOg+X2a2mm/MwDlmxoQDeq/Y824/U+zCNfsSDfO8O7+DZgkL2qoZuzMUlWb+DYzbYiVteIpc20ZZIE0PalK6lCr2zt8HM4K76rV5/7UVJvSAbHIDUeArSee/NLR9pq9Cy4t2oob0G4dm4uspEg6mYEtpbP4z85xAz5fZ1XesEycrRpKXblem6ciCEuUFgMGVGnFVxTUI1pkFfOopkoblJ93TdRwUMnHFOOsh37Gwsvk7Y/7W0r3+6wiFd6YJiPa79HzYEpUDSmyIw=="
48
+ }
49
+
50
+ KhipuRails.config = nil
51
+ KhipuRails.configure do |config|
52
+ config.add_receiver "1392", 'b174c94de0ec3ce4f1c3156e309de45e8ce0f9ef', :dev
53
+ end
54
+ end
55
+
56
+ it "fails to validate locally an invalid transaction" do
57
+ KhipuRails::NotificationValidator.is_valid?(@notification, :local).should == false
58
+ end
59
+
60
+ it "fails to validate an ivalid transaction via webserver" do
61
+ KhipuRails::NotificationValidator.is_valid?(@notification, :webservice).should == false
62
+ end
63
+ end
64
+ end