offsite_payments 2.5.0 → 2.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/offsite_payments/integrations/bit_pay.rb +0 -3
- data/lib/offsite_payments/integrations/epay.rb +1 -5
- data/lib/offsite_payments/integrations/mollie.rb +2 -2
- data/lib/offsite_payments/integrations/moneybookers.rb +1 -1
- data/lib/offsite_payments/integrations/paytm.rb +172 -95
- data/lib/offsite_payments/integrations/sage_pay_form.rb +2 -0
- data/lib/offsite_payments/notification.rb +3 -4
- data/lib/offsite_payments/version.rb +1 -1
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 73115ca84dcd4112cff7a48a4edf5d3be556c741
|
4
|
+
data.tar.gz: 5a30a82fdad39b6b31375fffbde56d51990d5713
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bef97be330df21fcd7c37d7a6ebcaa66f7712b773d43913b4ca87e9dfe713ff3bfb0e52da520fb0e57ebc183c4b79c1e0d0ad7f6f4103a934ea7cea2bef7b1e4
|
7
|
+
data.tar.gz: 274c019786d06ff21eecfa4316cb683e92b8a26296149be08c0fc1296761a1cf86561273f08424d671e395b163828f8b71350d4f3509ae1d3e9e2ce24f4012e6
|
@@ -89,7 +89,6 @@ module OffsitePayments #:nodoc:
|
|
89
89
|
|
90
90
|
def item_id
|
91
91
|
JSON.parse(params['posData'])['orderId']
|
92
|
-
rescue JSON::ParserError
|
93
92
|
end
|
94
93
|
|
95
94
|
def status
|
@@ -132,14 +131,12 @@ module OffsitePayments #:nodoc:
|
|
132
131
|
retrieved_json = JSON.parse(@raw).tap { |j| j.delete('currentTime') }
|
133
132
|
|
134
133
|
posted_json == retrieved_json
|
135
|
-
rescue JSON::ParserError
|
136
134
|
end
|
137
135
|
|
138
136
|
private
|
139
137
|
def parse(body)
|
140
138
|
@raw = body
|
141
139
|
@params = JSON.parse(@raw)
|
142
|
-
rescue JSON::ParserError
|
143
140
|
end
|
144
141
|
end
|
145
142
|
|
@@ -125,7 +125,7 @@ module OffsitePayments #:nodoc:
|
|
125
125
|
return false
|
126
126
|
end
|
127
127
|
|
128
|
-
%w(txnid orderid
|
128
|
+
%w(txnid orderid currency date time hash fraud payercountry issuercountry txnfee subscriptionid paymenttype cardno).each do |attr|
|
129
129
|
define_method(attr) do
|
130
130
|
params[attr]
|
131
131
|
end
|
@@ -135,10 +135,6 @@ module OffsitePayments #:nodoc:
|
|
135
135
|
CURRENCY_CODES.invert[params['currency']].to_s
|
136
136
|
end
|
137
137
|
|
138
|
-
def amount
|
139
|
-
Money.new(params['amount'].to_i, currency)
|
140
|
-
end
|
141
|
-
|
142
138
|
def generate_md5string
|
143
139
|
md5string = String.new
|
144
140
|
for line in @raw.split('&')
|
@@ -14,7 +14,7 @@ module OffsitePayments #:nodoc:
|
|
14
14
|
|
15
15
|
def get_request(resource, params = nil)
|
16
16
|
uri = URI.parse(MOLLIE_API_V1_URI + resource)
|
17
|
-
uri.query = params.map { |k,v| "#{CGI.escape(k)}=#{CGI.escape(v)}
|
17
|
+
uri.query = params.map { |k,v| "#{CGI.escape(k)}=#{CGI.escape(v)}"}.join('&') if params
|
18
18
|
headers = { "Authorization" => "Bearer #{token}", "Content-Type" => "application/json" }
|
19
19
|
JSON.parse(ssl_get(uri.to_s, headers))
|
20
20
|
end
|
@@ -29,4 +29,4 @@ module OffsitePayments #:nodoc:
|
|
29
29
|
|
30
30
|
end
|
31
31
|
end
|
32
|
-
end
|
32
|
+
end
|
@@ -2,7 +2,7 @@ module OffsitePayments #:nodoc:
|
|
2
2
|
module Integrations #:nodoc:
|
3
3
|
module Moneybookers
|
4
4
|
mattr_accessor :production_url
|
5
|
-
self.production_url = 'https://
|
5
|
+
self.production_url = 'https://pay.skrill.com'
|
6
6
|
|
7
7
|
def self.service_url
|
8
8
|
self.production_url
|
@@ -1,151 +1,216 @@
|
|
1
1
|
module OffsitePayments #:nodoc:
|
2
2
|
module Integrations #:nodoc:
|
3
3
|
module Paytm
|
4
|
-
|
4
|
+
CIPHER = 'AES-128-CBC'
|
5
|
+
SALT_ALPHABET = ['a'..'z', 'A'..'Z', '0'..'9'].flat_map { |i| i.to_a }
|
6
|
+
SALT_LENGTH = 4
|
7
|
+
STATIC_IV = '@@@@&&&&####$$$$'
|
8
|
+
|
9
|
+
mattr_accessor :test_url
|
10
|
+
mattr_accessor :production_url
|
11
|
+
|
12
|
+
self.test_url = 'https://pguat.paytm.com/oltp-web/processTransaction'
|
13
|
+
self.production_url = 'https://secure.paytm.in/oltp-web/processTransaction'
|
14
|
+
|
15
|
+
def self.service_url
|
16
|
+
OffsitePayments.mode == :production ? production_url : test_url
|
17
|
+
end
|
18
|
+
|
5
19
|
def self.notification(post, options = {})
|
6
20
|
Notification.new(post, options)
|
7
21
|
end
|
8
22
|
|
9
|
-
def self.return(
|
10
|
-
Return.new(
|
23
|
+
def self.return(post, options = {})
|
24
|
+
Return.new(post, options)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.checksum(hash, salt = nil)
|
28
|
+
if salt.nil?
|
29
|
+
salt = SALT_LENGTH.times.map { SALT_ALPHABET[SecureRandom.random_number(SALT_ALPHABET.length)] }.join
|
30
|
+
end
|
31
|
+
|
32
|
+
values = hash.sort.to_h.values
|
33
|
+
values << salt
|
34
|
+
Digest::SHA256.hexdigest(values.join('|')) + salt
|
11
35
|
end
|
12
36
|
|
13
|
-
def self.
|
14
|
-
|
37
|
+
def self.encrypt(data, key)
|
38
|
+
aes = OpenSSL::Cipher.new(CIPHER)
|
39
|
+
aes.encrypt
|
40
|
+
aes.key = key
|
41
|
+
aes.iv = STATIC_IV
|
42
|
+
|
43
|
+
encrypted_data = aes.update(data) + aes.final
|
44
|
+
Base64.strict_encode64(encrypted_data)
|
15
45
|
end
|
16
46
|
|
17
47
|
class Helper < OffsitePayments::Helper
|
18
|
-
|
19
|
-
'BIF' => 0,
|
20
|
-
'BYR' => 0,
|
21
|
-
'CLF' => 0,
|
22
|
-
'CLP' => 0,
|
23
|
-
'CVE' => 0,
|
24
|
-
'DJF' => 0,
|
25
|
-
'GNF' => 0,
|
26
|
-
'HUF' => 0,
|
27
|
-
'ISK' => 0,
|
28
|
-
'JPY' => 0,
|
29
|
-
'KMF' => 0,
|
30
|
-
'KRW' => 0,
|
31
|
-
'PYG' => 0,
|
32
|
-
'RWF' => 0,
|
33
|
-
'UGX' => 0,
|
34
|
-
'UYI' => 0,
|
35
|
-
'VND' => 0,
|
36
|
-
'VUV' => 0,
|
37
|
-
'XAF' => 0,
|
38
|
-
'XOF' => 0,
|
39
|
-
'XPF' => 0,
|
40
|
-
'BHD' => 3,
|
41
|
-
'IQD' => 3,
|
42
|
-
'JOD' => 3,
|
43
|
-
'KWD' => 3,
|
44
|
-
'LYD' => 3,
|
45
|
-
'OMR' => 3,
|
46
|
-
'TND' => 3,
|
47
|
-
'COU' => 4
|
48
|
-
}
|
48
|
+
CHECKSUM_FIELDS = %w(MID ORDER_ID CALLBACK_URL CUST_ID TXN_AMOUNT CHANNEL_ID INDUSTRY_TYPE_ID WEBSITE MERC_UNQ_REF).freeze
|
49
49
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
50
|
+
mapping :amount, 'TXN_AMOUNT'
|
51
|
+
mapping :account, 'MID'
|
52
|
+
mapping :order, 'MERC_UNQ_REF'
|
53
|
+
|
54
|
+
mapping :customer, :email => 'CUST_ID'
|
55
|
+
|
56
|
+
|
57
|
+
mapping :credential3, 'INDUSTRY_TYPE_ID'
|
58
|
+
mapping :credential4, 'WEBSITE'
|
59
|
+
mapping :channel_id, 'CHANNEL_ID'
|
60
|
+
mapping :return_url, 'CALLBACK_URL'
|
61
|
+
mapping :checksum, 'CHECKSUMHASH'
|
56
62
|
|
63
|
+
def initialize(order, account, options = {})
|
57
64
|
super
|
58
|
-
|
59
|
-
|
65
|
+
@options = options
|
66
|
+
@timestamp = Time.now.strftime('%Y%m%d%H%M%S')
|
60
67
|
|
61
|
-
|
62
|
-
|
68
|
+
add_field(mappings[:channel_id], "WEB")
|
69
|
+
add_field 'ORDER_ID', "#{order}-#{@timestamp.to_i}"
|
70
|
+
|
71
|
+
self.pg = 'CC'
|
63
72
|
end
|
64
73
|
|
65
74
|
def form_fields
|
66
|
-
|
75
|
+
sanitize_fields
|
76
|
+
@fields.merge(mappings[:checksum] => encrypt_checksum)
|
67
77
|
end
|
68
78
|
|
69
|
-
def
|
70
|
-
|
79
|
+
def encrypt_checksum
|
80
|
+
payload_items = CHECKSUM_FIELDS.reduce({}) do |items, field|
|
81
|
+
items[field] = @fields[field]
|
82
|
+
end
|
83
|
+
Paytm.encrypt(Paytm.checksum(payload_items), @options[:credential2])
|
71
84
|
end
|
72
85
|
|
73
|
-
def
|
74
|
-
|
86
|
+
def sanitize_fields
|
87
|
+
%w(email phone).each do |field|
|
88
|
+
@fields[field].gsub!(/[^a-zA-Z0-9\-_@\/\s.]/, '') if @fields[field]
|
89
|
+
end
|
75
90
|
end
|
91
|
+
end
|
76
92
|
|
77
|
-
|
78
|
-
|
93
|
+
class Notification < OffsitePayments::Notification
|
94
|
+
PAYTM_RESPONSE_PARAMS = %w(MID BANKTXNID TXNAMOUNT CURRENCY STATUS RESPCODE RESPMSG TXNDATE GATEWAYNAME BANKNAME PAYMENTMODE PROMO_CAMP_ID PROMO_STATUS PROMO_RESPCODE ORDERID TXNID REFUNDAMOUNT REFID MERC_UNQ_REF CUSTID).freeze
|
95
|
+
|
96
|
+
def initialize(post, options = {})
|
97
|
+
super
|
98
|
+
@secret_key = options[:credential2]
|
79
99
|
end
|
80
100
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
mapping :description, 'x_description'
|
85
|
-
mapping :invoice, 'x_invoice'
|
86
|
-
mapping :credential3, 'x_credential3'
|
87
|
-
mapping :credential4, 'x_credential4'
|
101
|
+
def complete?
|
102
|
+
status == 'Completed'
|
103
|
+
end
|
88
104
|
|
89
|
-
|
90
|
-
|
105
|
+
def status
|
106
|
+
if transaction_status.casecmp("TXN_SUCCESS").zero?
|
107
|
+
'Completed'
|
108
|
+
elsif transaction_status.casecmp("pending").zero?
|
109
|
+
'Pending'
|
110
|
+
else
|
111
|
+
'Failed'
|
112
|
+
end
|
113
|
+
end
|
91
114
|
|
92
|
-
|
93
|
-
|
94
|
-
|
115
|
+
def invoice_ok?(order_id)
|
116
|
+
order_id.to_s == invoice.to_s
|
117
|
+
end
|
95
118
|
|
96
|
-
|
119
|
+
# Order amount should be equal to gross
|
120
|
+
def amount_ok?(order_amount)
|
121
|
+
BigDecimal.new(original_gross) == order_amount
|
122
|
+
end
|
97
123
|
|
98
|
-
|
99
|
-
|
100
|
-
|
124
|
+
# Status of transaction return from the Paytm. List of possible values:
|
125
|
+
# <tt>TXN_SUCCESS</tt>::
|
126
|
+
# <tt>PENDING</tt>::
|
127
|
+
# <tt>TXN_FAILURE</tt>::
|
128
|
+
def transaction_status
|
129
|
+
@params['STATUS']
|
101
130
|
end
|
102
|
-
end
|
103
131
|
|
104
|
-
|
105
|
-
def
|
106
|
-
|
107
|
-
@key = options[:credential2]
|
132
|
+
# ID of this transaction (Paytm transaction id)
|
133
|
+
def transaction_id
|
134
|
+
@params['TXNID']
|
108
135
|
end
|
109
136
|
|
110
|
-
|
111
|
-
|
112
|
-
|
137
|
+
# Mode of Payment
|
138
|
+
#
|
139
|
+
# 'CC' for credit-card
|
140
|
+
# 'NB' for net-banking
|
141
|
+
# 'PPI' for wallet
|
142
|
+
def type
|
143
|
+
@params['PAYMENTMODE']
|
144
|
+
end
|
145
|
+
|
146
|
+
# What currency have we been dealing with
|
147
|
+
def currency
|
148
|
+
@params['CURRENCY']
|
113
149
|
end
|
114
150
|
|
115
151
|
def item_id
|
116
|
-
@params['
|
152
|
+
@params['MERC_UNQ_REF']
|
117
153
|
end
|
118
154
|
|
119
|
-
|
120
|
-
|
155
|
+
# This is the invoice which you passed to Paytm
|
156
|
+
def invoice
|
157
|
+
@params['MERC_UNQ_REF']
|
121
158
|
end
|
122
159
|
|
123
|
-
|
124
|
-
|
160
|
+
# Merchant Id provided by the Paytm
|
161
|
+
def account
|
162
|
+
@params['MID']
|
125
163
|
end
|
126
164
|
|
127
|
-
|
128
|
-
|
165
|
+
# original amount send by merchant
|
166
|
+
def original_gross
|
167
|
+
@params['TXNAMOUNT']
|
129
168
|
end
|
130
169
|
|
131
|
-
def
|
132
|
-
|
133
|
-
result && result.capitalize
|
170
|
+
def gross
|
171
|
+
parse_and_round_gross_amount(@params['TXNAMOUNT'])
|
134
172
|
end
|
135
173
|
|
136
174
|
def message
|
137
|
-
@params['
|
175
|
+
@params['RESPMSG']
|
176
|
+
end
|
177
|
+
|
178
|
+
def checksum
|
179
|
+
@params['CHECKSUMHASH']
|
138
180
|
end
|
139
181
|
|
140
|
-
def
|
141
|
-
|
182
|
+
def acknowledge
|
183
|
+
checksum_ok?
|
184
|
+
end
|
185
|
+
|
186
|
+
def checksum_ok?
|
187
|
+
normalized_data = checksum.delete("\n").tr(' ', '+')
|
188
|
+
encrypted_data = Base64.strict_decode64(normalized_data)
|
189
|
+
|
190
|
+
aes = OpenSSL::Cipher::Cipher.new(CIPHER)
|
191
|
+
aes.decrypt
|
192
|
+
aes.key = @secret_key
|
193
|
+
aes.iv = STATIC_IV
|
194
|
+
received_checksum = aes.update(encrypted_data) + aes.final
|
195
|
+
|
196
|
+
salt = received_checksum[-SALT_LENGTH..-1]
|
197
|
+
expected_params = @params.keep_if { |k| PAYTM_RESPONSE_PARAMS.include?(k) }.sort.to_h
|
198
|
+
expected_checksum = Paytm.checksum(expected_params, salt)
|
199
|
+
|
200
|
+
if received_checksum == expected_checksum
|
201
|
+
@message = @params['RESPMSG']
|
202
|
+
@params['RESPCODE'] == '01'
|
203
|
+
else
|
204
|
+
@message = 'Return checksum not matching the data provided'
|
205
|
+
false
|
206
|
+
end
|
142
207
|
end
|
143
208
|
|
144
209
|
private
|
145
210
|
|
146
|
-
def
|
147
|
-
|
148
|
-
|
211
|
+
def parse_and_round_gross_amount(amount)
|
212
|
+
rounded_amount = (amount.to_f * 100.0).round
|
213
|
+
sprintf('%.2f', rounded_amount / 100.00)
|
149
214
|
end
|
150
215
|
end
|
151
216
|
|
@@ -155,8 +220,20 @@ module OffsitePayments #:nodoc:
|
|
155
220
|
@notification = Notification.new(query_string, options)
|
156
221
|
end
|
157
222
|
|
223
|
+
def transaction_id
|
224
|
+
@notification.transaction_id
|
225
|
+
end
|
226
|
+
|
227
|
+
def status(order_id, order_amount)
|
228
|
+
if @notification.invoice_ok?(order_id) && @notification.amount_ok?(BigDecimal.new(order_amount))
|
229
|
+
@notification.status
|
230
|
+
else
|
231
|
+
'Mismatch'
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
158
235
|
def success?
|
159
|
-
@
|
236
|
+
status(@params['MERC_UNQ_REF'], @params['TXNAMOUNT']) == 'Completed'
|
160
237
|
end
|
161
238
|
|
162
239
|
def message
|
@@ -165,4 +242,4 @@ module OffsitePayments #:nodoc:
|
|
165
242
|
end
|
166
243
|
end
|
167
244
|
end
|
168
|
-
end
|
245
|
+
end
|
@@ -30,11 +30,10 @@ module OffsitePayments #:nodoc:
|
|
30
30
|
(gross.to_f * 100.0).round
|
31
31
|
end
|
32
32
|
|
33
|
-
# This combines the gross and currency and returns a proper Money object.
|
34
|
-
# this requires the money library located at http://rubymoney.github.io/money/
|
35
33
|
def amount
|
36
|
-
|
37
|
-
return Money.
|
34
|
+
amount = gross ? gross.to_d : 0
|
35
|
+
return Money.from_amount(amount, currency) rescue ArgumentError
|
36
|
+
return Money.from_amount(amount) # maybe you have an own money object which doesn't take a currency?
|
38
37
|
end
|
39
38
|
|
40
39
|
# reset the notification.
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: offsite_payments
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tobias Luetke
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-08-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -19,7 +19,7 @@ dependencies:
|
|
19
19
|
version: 3.2.14
|
20
20
|
- - "<"
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version:
|
22
|
+
version: '5.2'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -29,7 +29,7 @@ dependencies:
|
|
29
29
|
version: 3.2.14
|
30
30
|
- - "<"
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version:
|
32
|
+
version: '5.2'
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
34
|
name: i18n
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -115,7 +115,7 @@ dependencies:
|
|
115
115
|
version: 3.2.20
|
116
116
|
- - "<"
|
117
117
|
- !ruby/object:Gem::Version
|
118
|
-
version:
|
118
|
+
version: '5.2'
|
119
119
|
type: :runtime
|
120
120
|
prerelease: false
|
121
121
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -125,7 +125,7 @@ dependencies:
|
|
125
125
|
version: 3.2.20
|
126
126
|
- - "<"
|
127
127
|
- !ruby/object:Gem::Version
|
128
|
-
version:
|
128
|
+
version: '5.2'
|
129
129
|
- !ruby/object:Gem::Dependency
|
130
130
|
name: rake
|
131
131
|
requirement: !ruby/object:Gem::Requirement
|