offsite_payments_poli_pay 0.1.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: 8befcd9b2c4decfa01e2a3b0570ed2afc11040c5
4
+ data.tar.gz: 42ebf5a83632cf1d5eccce6908126663cb677476
5
+ SHA512:
6
+ metadata.gz: 6d454dd6b05a50dbdc7482427bf26283c5c14901af8c3ea9774a5bda9efd720528ce8d6129a092c16de93b7c0ca3b476f4619c8b7c143018c0c6ded843df06f8
7
+ data.tar.gz: d3c4011eec91e58ab9bcc93e9e5722f4d03da9103c452e0c9d0389917624a0df04e1821a5e8986d68bcfa383ad6a770934768f4c19077e12f79c1dc2b4c02aa8
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2005-2014 Tobias Luetke
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,31 @@
1
+ # Offsite Payments
2
+
3
+ Offsite Payments is an extraction from the ecommerce system [Shopify](http://www.shopify.com). Shopify's requirements for a simple and unified API to handle dozens of different offsite payment pages (often called hosted payment pages) with very different exposed APIs was the chief principle in designing the library.
4
+
5
+ It was developed for usage in Ruby on Rails web applications and integrates seamlessly
6
+ as a Rails plugin. It should also work as a stand alone Ruby library, but much of the benefit is in the ActionView helpers which are Rails-specific.
7
+
8
+ This gem provides integration for [POLi Payments](https://www.polipayments.com/)
9
+ through the Offsite Payments gem.
10
+
11
+ ## Installation
12
+
13
+ ### From Git
14
+
15
+ You can check out the latest source from git:
16
+
17
+ git clone https://github.com/sealink/offsite_payments_poli_pay.git
18
+
19
+ ### From RubyGems
20
+
21
+ Installation from RubyGems:
22
+
23
+ gem install offsite_payments_poli_pay
24
+
25
+ Or, if you're using Bundler, just add the following to your Gemfile:
26
+
27
+ gem 'offsite_payments_poli_pay'
28
+
29
+ ## Misc.
30
+
31
+ - This library is MIT licensed.
@@ -0,0 +1,266 @@
1
+ # Requirements from:
2
+ # https://polipayments.com/assets/docs/POLiWebServicesMIG.pdf
3
+
4
+ module OffsitePayments
5
+ module Integrations
6
+ module PoliPay
7
+ def self.notification(post, options = {})
8
+ Notification.new(post, options)
9
+ end
10
+
11
+ def self.return(query_string, options = {})
12
+ Return.new(query_string, options)
13
+ end
14
+
15
+ def self.sign(fields, key)
16
+ OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, key, fields.sort.join)
17
+ end
18
+
19
+ class Interface
20
+ include ActiveUtils::PostsData # ssl_get/post
21
+
22
+ def self.base_url
23
+ "https://poliapi.apac.paywithpoli.com/api"
24
+ end
25
+
26
+ def initialize(login, password)
27
+ @login = login
28
+ @password = password
29
+ end
30
+
31
+ private
32
+
33
+ def standard_headers
34
+ authorization = Base64.encode64("#{@login}:#{@password}")
35
+ {
36
+ 'Content-Type' => 'application/json',
37
+ 'Authorization' => "Basic #{authorization}"
38
+ }
39
+ end
40
+
41
+ def parse_response(raw_response)
42
+ JSON.parse(raw_response)
43
+ end
44
+
45
+ class RequestError < StandardError
46
+ attr_reader :exception, :message, :error_message, :error_code
47
+
48
+ def initialize(exception)
49
+ @exception = exception
50
+
51
+ @response = JSON.parse(exception.response.body)
52
+ @success = @response['Success']
53
+ @message = @response['Message']
54
+ @error_message = @response['ErrorMessage']
55
+ @error_code = @response['ErrorCode']
56
+ end
57
+
58
+ def success?
59
+ !!@success
60
+ end
61
+
62
+ def errors
63
+ fail NotImplementedError, "This method must be implemented on the subclass"
64
+ end
65
+
66
+ def error_code_text
67
+ errors[@error_code.to_s]
68
+ end
69
+ end
70
+ end
71
+
72
+ class UrlInterface < Interface
73
+ def self.url
74
+ "#{base_url}/Transaction/Initiate"
75
+ end
76
+
77
+ def call(options)
78
+ raw_response = ssl_post(self.class.url, options.to_json, standard_headers)
79
+ result = parse_response(raw_response)
80
+ result['NavigateURL']
81
+ rescue ActiveUtils::ResponseError => e
82
+ raise UrlRequestError, e
83
+ end
84
+
85
+ class UrlRequestError < RequestError
86
+ ERRORS = {
87
+ '14050' => "A transaction-specific error has occurred",
88
+ '14053' => "The amount specified exceeds the individual transaction limit set by the merchant",
89
+ '14054' => "The amount specified will cause the daily transaction limit to be exceeded",
90
+ '14055' => "General failure to initiate a transaction",
91
+ '14056' => "Error in merchant-defined data",
92
+ '14057' => "One or more values specified have failed a validation check",
93
+ '14058' => "The monetary amount specified is invalid",
94
+ '14059' => "A URL provided for one or more fields was not formatted correctly",
95
+ '14060' => "The currency code supplied is not supported by POLi or the specific merchant",
96
+ '14061' => "The MerchantReference field contains invalid characters",
97
+ '14062' => "One or more fields that are mandatory did not have values specified",
98
+ '14099' => "An unexpected error has occurred within transaction functionality"
99
+ }
100
+
101
+ def errors
102
+ ERRORS
103
+ end
104
+ end
105
+ end
106
+
107
+ class QueryInterface < Interface
108
+ def self.url(token)
109
+ "#{base_url}/Transaction/GetTransaction?token=#{CGI.escape(token)}"
110
+ end
111
+
112
+ def call(token)
113
+ raise ArgumentError, "Token must be specified" if token.blank?
114
+ raw_response = ssl_get(self.class.url(token), standard_headers)
115
+ parse_response(raw_response)
116
+ rescue ActiveUtils::ResponseError => e
117
+ raise QueryRequestError, e
118
+ end
119
+
120
+ class QueryRequestError < RequestError
121
+ ERRORS = {
122
+ '14050' => "Transaction was initiated by another merchant or another transaction-based error",
123
+ '14051' => "The transaction was not found",
124
+ '14052' => "The token provided was incomplete, corrupted or doesn't exist"
125
+ }
126
+
127
+ def errors
128
+ ERRORS
129
+ end
130
+ end
131
+ end
132
+
133
+ class FinancialInstitutionsInterface < Interface
134
+ def self.url
135
+ "#{base_url}/Entity/GetFinancialInstitutions"
136
+ end
137
+
138
+ def call
139
+ raw_response = ssl_get(self.class.url, standard_headers)
140
+ result = parse_response(raw_response)
141
+ result.map { |attr| FinancialInstitution.new(attr) }
142
+ end
143
+ end
144
+
145
+ class Helper < OffsitePayments::Helper
146
+ SUPPORTED_CURRENCIES = %w[AUD NZD]
147
+
148
+ mapping :notify_url, 'NotificationUrl'
149
+ mapping :amount, 'Amount'
150
+ mapping :currency, 'CurrencyCode'
151
+ mapping :order, 'MerchantReference'
152
+
153
+ attr_reader :token_parameters
154
+
155
+ def initialize(order, account, options = {})
156
+ @login = account
157
+ @password = options.fetch(:password)
158
+ @options = options.except(:password).merge(order: order)
159
+ check_order!(order)
160
+ super(order, account, options.except(
161
+ :homepage_url, :failure_url, :cancellation_url, :password))
162
+ add_field 'MerchantDateTime', current_time_utc
163
+ add_field 'Timeout', options[:timeout] if options[:timeout] # or defaults
164
+ add_field 'SuccessUrl', options.fetch(:success_url) { options.fetch(:return_url) }
165
+ add_field 'FailureUrl', options.fetch(:failure_url) { options.fetch(:return_url) }
166
+ add_field 'CancellationUrl', options.fetch(:cancellation_url) { options.fetch(:return_url) }
167
+ add_field 'MerchantHomepageURL', options.fetch(:homepage_url)
168
+ end
169
+
170
+ def check_order!(order)
171
+ invalid = order.match /[^[[:alnum:]]_.:?\/\-|]/
172
+ return unless invalid
173
+ fail ArgumentError,
174
+ 'order not valid format, must only include alphanumeric, ' \
175
+ 'underscore (_), period (.), colon (:), question mark (?), ' \
176
+ 'forward slash (/), hyphen (-) or pipe (|)'
177
+ end
178
+
179
+ def current_time_utc
180
+ Time.current.utc.strftime("%Y-%m-%dT%H:%M:%S")
181
+ end
182
+
183
+ def currency(symbol)
184
+ unless SUPPORTED_CURRENCIES.include?(symbol)
185
+ raise ArgumentError, "Unsupported currency"
186
+ end
187
+ add_field mappings[:currency], symbol
188
+ end
189
+
190
+ def amount(money)
191
+ add_field mappings[:amount], '%.2f' % money.to_f.round(2)
192
+ end
193
+
194
+ def credential_based_url
195
+ UrlInterface.new(@login, @password).call(form_fields)
196
+ end
197
+ end
198
+
199
+ # See
200
+ # http://www.polipaymentdeveloper.com/gettransaction#gettransaction_response
201
+ class Notification < OffsitePayments::Notification
202
+ def initialize(params, options = {})
203
+ # POLi nudge uses Token, redirect use token
204
+ token = params.fetch('Token') { params.fetch('token') }
205
+ @params = QueryInterface.new(options[:login], options[:password]).call(token)
206
+ end
207
+
208
+ def acknowledge
209
+ true # always valid as we fetch direct from poli
210
+ end
211
+
212
+ def complete?
213
+ @params['TransactionStatusCode'] == 'Completed'
214
+ end
215
+
216
+ def success?
217
+ complete? && gross && gross > 0
218
+ end
219
+
220
+ def gross
221
+ @params['AmountPaid']
222
+ end
223
+
224
+ def currency
225
+ @params['CurrencyCode']
226
+ end
227
+
228
+ def order_id
229
+ @params['MerchantReference']
230
+ end
231
+
232
+ def transaction_id
233
+ @params['TransactionRefNo']
234
+ end
235
+
236
+ # There is only a message on failure
237
+ # http://www.polipaymentdeveloper.com/initiate#initiatetransaction_response
238
+ def message
239
+ @params['ErrorMessage']
240
+ end
241
+ end
242
+
243
+ class Return < OffsitePayments::Return
244
+ def initialize(query_string, options={})
245
+ @notification = Notification.new(query_string, options)
246
+ end
247
+ end
248
+
249
+ # See
250
+ # http://www.polipaymentdeveloper.com/ficode#getfinancialinstitutions_response
251
+ class FinancialInstitution
252
+ attr_reader :name, :code
253
+
254
+ def initialize(attr)
255
+ @name = attr.fetch('Name')
256
+ @code = attr.fetch('Code')
257
+ @online = attr.fetch('Online')
258
+ end
259
+
260
+ def online?
261
+ !!@online
262
+ end
263
+ end
264
+ end
265
+ end
266
+ end
@@ -0,0 +1,7 @@
1
+ module OffsitePayments
2
+ module Integration
3
+ module PoliPay
4
+ VERSION = '0.1.0'
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ class OffsitePaymentsPoliPay
2
+ require 'offsite_payments'
3
+ require_relative 'offsite_payments/integrations/poli_pay'
4
+ end
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: offsite_payments_poli_pay
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Stefan Cooper
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-03-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: offsite_payments
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.7'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.7'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: This gem extends the activemerchant offsite_payments gem providing integration
56
+ of PoliPay.
57
+ email: stefan.cooper@sealink.com.au
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - MIT-LICENSE
63
+ - README.md
64
+ - lib/offsite_payments/integrations/poli_pay.rb
65
+ - lib/offsite_payments/version.rb
66
+ - lib/offsite_payments_poli_pay.rb
67
+ homepage: https://github.com/sealink/offsite_payments_poli_pay
68
+ licenses:
69
+ - MIT
70
+ metadata: {}
71
+ post_install_message:
72
+ rdoc_options: []
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ required_rubygems_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ requirements: []
86
+ rubyforge_project:
87
+ rubygems_version: 2.6.8
88
+ signing_key:
89
+ specification_version: 4
90
+ summary: PoliPay integration for the activemerchant offsite_payments gem.
91
+ test_files: []