offsite_payments 2.5.0 → 2.6.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.
- 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
|