better_offsite_payments 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +76 -0
- data/lib/offsite_payments.rb +39 -0
- data/lib/offsite_payments/action_view_helper.rb +72 -0
- data/lib/offsite_payments/helper.rb +120 -0
- data/lib/offsite_payments/integrations.rb +14 -0
- data/lib/offsite_payments/integrations/a1agregator.rb +245 -0
- data/lib/offsite_payments/integrations/authorize_net_sim.rb +580 -0
- data/lib/offsite_payments/integrations/bit_pay.rb +150 -0
- data/lib/offsite_payments/integrations/bogus.rb +32 -0
- data/lib/offsite_payments/integrations/chronopay.rb +283 -0
- data/lib/offsite_payments/integrations/citrus.rb +227 -0
- data/lib/offsite_payments/integrations/coinbase.rb +172 -0
- data/lib/offsite_payments/integrations/direc_pay.rb +332 -0
- data/lib/offsite_payments/integrations/directebanking.rb +237 -0
- data/lib/offsite_payments/integrations/doku.rb +171 -0
- data/lib/offsite_payments/integrations/dotpay.rb +166 -0
- data/lib/offsite_payments/integrations/dwolla.rb +160 -0
- data/lib/offsite_payments/integrations/e_payment_plans.rb +146 -0
- data/lib/offsite_payments/integrations/easy_pay.rb +137 -0
- data/lib/offsite_payments/integrations/epay.rb +161 -0
- data/lib/offsite_payments/integrations/first_data.rb +133 -0
- data/lib/offsite_payments/integrations/gestpay.rb +205 -0
- data/lib/offsite_payments/integrations/hi_trust.rb +179 -0
- data/lib/offsite_payments/integrations/ipay88.rb +251 -0
- data/lib/offsite_payments/integrations/klarna.rb +275 -0
- data/lib/offsite_payments/integrations/liqpay.rb +216 -0
- data/lib/offsite_payments/integrations/maksuturva.rb +231 -0
- data/lib/offsite_payments/integrations/megakassa.rb +184 -0
- data/lib/offsite_payments/integrations/mollie.rb +32 -0
- data/lib/offsite_payments/integrations/mollie_ideal.rb +194 -0
- data/lib/offsite_payments/integrations/mollie_mistercash.rb +143 -0
- data/lib/offsite_payments/integrations/molpay.rb +193 -0
- data/lib/offsite_payments/integrations/moneybookers.rb +199 -0
- data/lib/offsite_payments/integrations/nochex.rb +228 -0
- data/lib/offsite_payments/integrations/pag_seguro.rb +268 -0
- data/lib/offsite_payments/integrations/paxum.rb +114 -0
- data/lib/offsite_payments/integrations/pay_fast.rb +269 -0
- data/lib/offsite_payments/integrations/paydollar.rb +142 -0
- data/lib/offsite_payments/integrations/payflow_link.rb +194 -0
- data/lib/offsite_payments/integrations/paypal.rb +362 -0
- data/lib/offsite_payments/integrations/paypal_payments_advanced.rb +23 -0
- data/lib/offsite_payments/integrations/paysbuy.rb +71 -0
- data/lib/offsite_payments/integrations/payu_in.rb +276 -0
- data/lib/offsite_payments/integrations/payu_in_paisa.rb +46 -0
- data/lib/offsite_payments/integrations/platron.rb +153 -0
- data/lib/offsite_payments/integrations/pxpay.rb +273 -0
- data/lib/offsite_payments/integrations/quickpay.rb +232 -0
- data/lib/offsite_payments/integrations/rbkmoney.rb +110 -0
- data/lib/offsite_payments/integrations/realex_offsite.rb +317 -0
- data/lib/offsite_payments/integrations/robokassa.rb +154 -0
- data/lib/offsite_payments/integrations/sage_pay_form.rb +431 -0
- data/lib/offsite_payments/integrations/two_checkout.rb +329 -0
- data/lib/offsite_payments/integrations/universal.rb +190 -0
- data/lib/offsite_payments/integrations/valitor.rb +200 -0
- data/lib/offsite_payments/integrations/verkkomaksut.rb +143 -0
- data/lib/offsite_payments/integrations/web_pay.rb +186 -0
- data/lib/offsite_payments/integrations/webmoney.rb +119 -0
- data/lib/offsite_payments/integrations/wirecard_checkout_page.rb +359 -0
- data/lib/offsite_payments/integrations/world_pay.rb +280 -0
- data/lib/offsite_payments/integrations/yandex_money.rb +175 -0
- data/lib/offsite_payments/notification.rb +71 -0
- data/lib/offsite_payments/return.rb +37 -0
- data/lib/offsite_payments/version.rb +3 -0
- metadata +297 -0
@@ -0,0 +1,251 @@
|
|
1
|
+
module OffsitePayments #:nodoc:
|
2
|
+
module Integrations #:nodoc:
|
3
|
+
module Ipay88
|
4
|
+
CANCELLED_ERROR_DESCRIPTION = 'Customer Cancel Transaction'
|
5
|
+
|
6
|
+
def self.service_url
|
7
|
+
"https://www.mobile88.com/epayment/entry.asp"
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.requery_url
|
11
|
+
"https://www.mobile88.com/epayment/enquiry.asp"
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.return(query_string, options={})
|
15
|
+
Return.new(query_string, options)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.notification(post, options = {})
|
19
|
+
Notification.new(post, options)
|
20
|
+
end
|
21
|
+
|
22
|
+
class Helper < OffsitePayments::Helper
|
23
|
+
include ActiveUtils::RequiresParameters
|
24
|
+
|
25
|
+
# Currencies supported
|
26
|
+
# MYR (Malaysian Ringgit - for all payment methods except China Union Pay and PayPal)
|
27
|
+
# USD (US Dollar - only for PayPal)
|
28
|
+
# CNY (Yuan Renminbi - only for China Union Pay)
|
29
|
+
SUPPORTED_CURRENCIES = %w[MYR USD CNY]
|
30
|
+
|
31
|
+
# Languages supported
|
32
|
+
# ISO-8859-1 (English)
|
33
|
+
# UTF-8 (Unicode)
|
34
|
+
# GB2312 (Chinese Simplified)
|
35
|
+
# GD18030 (Chinese Simplified)
|
36
|
+
# BIG5 (Chinese Traditional)
|
37
|
+
SUPPORTED_LANGS = %w[ISO-8859-1 UTF-8 GB2312 GD18030 BIG5]
|
38
|
+
|
39
|
+
# Payment methods supported
|
40
|
+
# 8 (Alliance Online Transfer)
|
41
|
+
# 10 (AmBank)
|
42
|
+
# 21 (China Union Pay)
|
43
|
+
# 20 (CIMB Click)
|
44
|
+
# 2 (Credit Card MYR)
|
45
|
+
# 16 (FPX)
|
46
|
+
# 15 (Hong Leong Bank Transfer)
|
47
|
+
# 6 (Maybank2u.com)
|
48
|
+
# 23 (MEPS Cash)
|
49
|
+
# 17 (Mobile Money)
|
50
|
+
# 33 (PayPal)
|
51
|
+
# 14 (RHB)
|
52
|
+
PAYMENT_METHODS = %w[8 10 21 20 2 16 15 6 23 17 33 14]
|
53
|
+
|
54
|
+
attr_reader :amount_in_cents, :merchant_key
|
55
|
+
|
56
|
+
def initialize(order, account, options = {})
|
57
|
+
requires!(options, :amount, :currency, :credential2)
|
58
|
+
@merchant_key = options[:credential2]
|
59
|
+
@amount_in_cents = options[:amount]
|
60
|
+
super
|
61
|
+
add_field mappings[:signature], signature
|
62
|
+
end
|
63
|
+
|
64
|
+
def amount_in_dollars
|
65
|
+
sprintf("%.2f", @amount_in_cents.to_f/100)
|
66
|
+
end
|
67
|
+
|
68
|
+
def amount=(money)
|
69
|
+
@amount_in_cents = money.respond_to?(:cents) ? money.cents : money
|
70
|
+
raise ArgumentError, "amount must be a Money object or an integer" if money.is_a?(String)
|
71
|
+
raise ActionViewHelperError, "amount must be greater than $0.00" if @amount_in_cents.to_i <= 0
|
72
|
+
|
73
|
+
add_field mappings[:amount], amount_in_dollars
|
74
|
+
end
|
75
|
+
|
76
|
+
def currency(symbol)
|
77
|
+
raise ArgumentError, "unsupported currency" unless SUPPORTED_CURRENCIES.include?(symbol)
|
78
|
+
add_field mappings[:currency], symbol
|
79
|
+
end
|
80
|
+
|
81
|
+
def language(lang)
|
82
|
+
raise ArgumentError, "unsupported language" unless SUPPORTED_LANGS.include?(lang)
|
83
|
+
add_field mappings[:language], lang
|
84
|
+
end
|
85
|
+
|
86
|
+
def payment(pay_method)
|
87
|
+
raise ArgumentError, "unsupported payment method" unless PAYMENT_METHODS.include?(pay_method.to_s)
|
88
|
+
add_field mappings[:payment], pay_method
|
89
|
+
end
|
90
|
+
|
91
|
+
def customer(params = {})
|
92
|
+
add_field(mappings[:customer][:name], "#{params[:first_name]} #{params[:last_name]}")
|
93
|
+
add_field(mappings[:customer][:email], params[:email])
|
94
|
+
add_field(mappings[:customer][:phone], params[:phone])
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.sign(str)
|
98
|
+
[Digest::SHA1.digest(str)].pack("m").chomp
|
99
|
+
end
|
100
|
+
|
101
|
+
def signature
|
102
|
+
self.class.sign(self.sig_components)
|
103
|
+
end
|
104
|
+
|
105
|
+
mapping :account, "MerchantCode"
|
106
|
+
mapping :amount, "Amount"
|
107
|
+
mapping :currency, "Currency"
|
108
|
+
mapping :order, "RefNo"
|
109
|
+
mapping :description, "ProdDesc"
|
110
|
+
mapping :customer, :name => "UserName",
|
111
|
+
:email => "UserEmail",
|
112
|
+
:phone => "UserContact"
|
113
|
+
mapping :remark, "Remark"
|
114
|
+
mapping :language, "Lang"
|
115
|
+
mapping :payment, "PaymentId"
|
116
|
+
mapping :return_url, "ResponseURL"
|
117
|
+
mapping :notify_url, "BackendURL"
|
118
|
+
mapping :signature, "Signature"
|
119
|
+
|
120
|
+
protected
|
121
|
+
|
122
|
+
def sig_components
|
123
|
+
components = [merchant_key]
|
124
|
+
components << fields[mappings[:account]]
|
125
|
+
components << fields[mappings[:order]]
|
126
|
+
components << amount_in_dollars.gsub(/[.,]/, '')
|
127
|
+
components << fields[mappings[:currency]]
|
128
|
+
components.join
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
class Notification < OffsitePayments::Notification
|
133
|
+
include ActiveUtils::PostsData
|
134
|
+
|
135
|
+
def status
|
136
|
+
if params["Status"] == '1'
|
137
|
+
'Completed'
|
138
|
+
else
|
139
|
+
error == CANCELLED_ERROR_DESCRIPTION ? 'Cancelled' : 'Failed'
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def complete?
|
144
|
+
status == 'Completed'
|
145
|
+
end
|
146
|
+
|
147
|
+
def item_id
|
148
|
+
params["RefNo"]
|
149
|
+
end
|
150
|
+
|
151
|
+
def gross
|
152
|
+
params["Amount"].try(:gsub, /,(?=\d{3}\b)/, '')
|
153
|
+
end
|
154
|
+
|
155
|
+
def currency
|
156
|
+
params["Currency"]
|
157
|
+
end
|
158
|
+
|
159
|
+
def account
|
160
|
+
params["MerchantCode"]
|
161
|
+
end
|
162
|
+
|
163
|
+
def payment
|
164
|
+
params["PaymentId"].to_i
|
165
|
+
end
|
166
|
+
|
167
|
+
def remark
|
168
|
+
params["Remark"]
|
169
|
+
end
|
170
|
+
|
171
|
+
def transaction_id
|
172
|
+
params["TransId"]
|
173
|
+
end
|
174
|
+
|
175
|
+
def auth_code
|
176
|
+
params["AuthCode"]
|
177
|
+
end
|
178
|
+
|
179
|
+
def error
|
180
|
+
params["ErrDesc"]
|
181
|
+
end
|
182
|
+
|
183
|
+
def signature
|
184
|
+
params["Signature"]
|
185
|
+
end
|
186
|
+
|
187
|
+
def secure?
|
188
|
+
generated_signature == signature
|
189
|
+
end
|
190
|
+
|
191
|
+
def success?
|
192
|
+
status == 'Completed'
|
193
|
+
end
|
194
|
+
|
195
|
+
def acknowledge
|
196
|
+
secure? && (!success? || requery == "00")
|
197
|
+
end
|
198
|
+
|
199
|
+
protected
|
200
|
+
|
201
|
+
def generated_signature
|
202
|
+
Helper.sign(sig_components)
|
203
|
+
end
|
204
|
+
|
205
|
+
def sig_components
|
206
|
+
components = [@options[:credential2]]
|
207
|
+
[:account, :payment, :item_id, :amount_in_cents, :currency].each do |i|
|
208
|
+
components << send(i)
|
209
|
+
end
|
210
|
+
components << params["Status"]
|
211
|
+
components.join
|
212
|
+
end
|
213
|
+
|
214
|
+
def requery
|
215
|
+
data = { "MerchantCode" => account, "RefNo" => item_id, "Amount" => gross }
|
216
|
+
params = parameterize(data)
|
217
|
+
ssl_post Ipay88.requery_url, params, { "Content-Length" => params.size.to_s, "User-Agent" => "Active Merchant -- http://activemerchant.org" }
|
218
|
+
end
|
219
|
+
|
220
|
+
private
|
221
|
+
|
222
|
+
def parameterize(params)
|
223
|
+
params.reject { |k, v| v.blank? }.keys.sort.collect { |key| "#{key}=#{CGI.escape(params[key].to_s)}" }.join("&")
|
224
|
+
end
|
225
|
+
|
226
|
+
def amount_in_cents
|
227
|
+
@amount_in_cents ||= (gross || "").gsub(/[.,]/, "")
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
class Return < OffsitePayments::Return
|
232
|
+
def initialize(query_string, options = {})
|
233
|
+
super
|
234
|
+
@notification = Notification.new(query_string, options)
|
235
|
+
end
|
236
|
+
|
237
|
+
def success?
|
238
|
+
params["Status"] == "1"
|
239
|
+
end
|
240
|
+
|
241
|
+
def cancelled?
|
242
|
+
params["ErrDesc"] == CANCELLED_ERROR_DESCRIPTION
|
243
|
+
end
|
244
|
+
|
245
|
+
def message
|
246
|
+
params["ErrDesc"]
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
@@ -0,0 +1,275 @@
|
|
1
|
+
module OffsitePayments #:nodoc:
|
2
|
+
module Integrations #:nodoc:
|
3
|
+
module Klarna
|
4
|
+
mattr_accessor :service_url
|
5
|
+
self.service_url = 'https://api.hostedcheckout.io/shopify/payment'
|
6
|
+
|
7
|
+
REQUIRED_FIELDS = %w(amount checkout_token merchant_base_uri merchant_checkout_uri merchant_confirmation_uri merchant_id merchant_terms_uri purchase_currency)
|
8
|
+
|
9
|
+
def self.notification(post_body, options = {})
|
10
|
+
Notification.new(post_body, options)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.return(query_string, options = {})
|
14
|
+
Return.new(query_string, options)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.cart_items_payload(fields, cart_items)
|
18
|
+
check_required_fields!(fields)
|
19
|
+
|
20
|
+
payload = ""
|
21
|
+
REQUIRED_FIELDS.sort.each do |field|
|
22
|
+
payload << fields[field].to_s
|
23
|
+
end
|
24
|
+
|
25
|
+
payload
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.sign(fields, cart_items, shared_secret)
|
29
|
+
payload = cart_items_payload(fields, cart_items)
|
30
|
+
|
31
|
+
digest(payload, shared_secret)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.digest(payload, shared_secret)
|
35
|
+
Digest::SHA256.base64digest(payload + shared_secret.to_s)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def self.check_required_fields!(fields)
|
41
|
+
REQUIRED_FIELDS.each do |required_field|
|
42
|
+
raise ArgumentError, "Missing required field #{required_field}" if fields[required_field].nil?
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class Helper < OffsitePayments::Helper
|
47
|
+
mapping :currency, 'purchase_currency'
|
48
|
+
mapping :cancel_return_url, ['merchant_terms_uri', 'merchant_checkout_uri', 'merchant_base_uri']
|
49
|
+
mapping :account, 'merchant_id'
|
50
|
+
mapping :customer, email: 'shipping_address_email'
|
51
|
+
mapping :checkout_token, 'checkout_token'
|
52
|
+
mapping :amount, 'amount'
|
53
|
+
|
54
|
+
def initialize(order, account, options = {})
|
55
|
+
super
|
56
|
+
@shared_secret = options[:credential2]
|
57
|
+
@order = order
|
58
|
+
|
59
|
+
add_field('platform_type', application_id)
|
60
|
+
add_field('test_mode', test?.to_s)
|
61
|
+
end
|
62
|
+
|
63
|
+
def notify_url(url)
|
64
|
+
url = append_order_query_param(url)
|
65
|
+
add_field('merchant_push_uri', url)
|
66
|
+
end
|
67
|
+
|
68
|
+
def return_url(url)
|
69
|
+
url = append_order_query_param(url)
|
70
|
+
add_field('merchant_confirmation_uri', url)
|
71
|
+
end
|
72
|
+
|
73
|
+
def line_item(item)
|
74
|
+
@line_items ||= []
|
75
|
+
@line_items << item
|
76
|
+
|
77
|
+
i = @line_items.size - 1
|
78
|
+
|
79
|
+
add_field("cart_item-#{i}_type", type_for(item))
|
80
|
+
add_field("cart_item-#{i}_reference", item.fetch(:reference, ''))
|
81
|
+
add_field("cart_item-#{i}_name", item.fetch(:name, ''))
|
82
|
+
add_field("cart_item-#{i}_quantity", item.fetch(:quantity, ''))
|
83
|
+
add_field("cart_item-#{i}_unit_price", tax_included_unit_price(item)).to_s
|
84
|
+
add_field("cart_item-#{i}_discount_rate", item.fetch(:discount_rate, ''))
|
85
|
+
add_field("cart_item-#{i}_tax_rate", tax_rate_for(item)).to_s
|
86
|
+
|
87
|
+
@fields
|
88
|
+
end
|
89
|
+
|
90
|
+
def billing_address(billing_fields)
|
91
|
+
country = billing_fields[:country]
|
92
|
+
|
93
|
+
add_field('purchase_country', country)
|
94
|
+
add_field('locale', guess_locale_based_on_country(country))
|
95
|
+
end
|
96
|
+
|
97
|
+
def shipping_address(shipping_fields)
|
98
|
+
add_field('shipping_address_given_name', shipping_fields[:first_name])
|
99
|
+
add_field('shipping_address_family_name', shipping_fields[:last_name])
|
100
|
+
|
101
|
+
street_address = [shipping_fields[:address1], shipping_fields[:address2]].compact.join(', ')
|
102
|
+
add_field('shipping_address_street_address', street_address)
|
103
|
+
|
104
|
+
add_field('shipping_address_postal_code', shipping_fields[:zip])
|
105
|
+
add_field('shipping_address_city', shipping_fields[:city])
|
106
|
+
add_field('shipping_address_country', shipping_fields[:country])
|
107
|
+
add_field('shipping_address_phone', shipping_fields[:phone])
|
108
|
+
end
|
109
|
+
|
110
|
+
def form_fields
|
111
|
+
sign_fields
|
112
|
+
|
113
|
+
super
|
114
|
+
end
|
115
|
+
|
116
|
+
def sign_fields
|
117
|
+
merchant_digest = Klarna.sign(@fields, @line_items, @shared_secret)
|
118
|
+
add_field('merchant_digest', merchant_digest)
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
|
123
|
+
def type_for(item)
|
124
|
+
case item.fetch(:type, '')
|
125
|
+
when 'shipping'
|
126
|
+
'shipping_fee'
|
127
|
+
when 'line item'
|
128
|
+
'physical'
|
129
|
+
when 'discount'
|
130
|
+
'discount'
|
131
|
+
else
|
132
|
+
raise StandardError, "Unable to determine type for item #{item.to_yaml}"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def append_order_query_param(url)
|
137
|
+
u = URI.parse(url)
|
138
|
+
params = Rack::Utils.parse_nested_query(u.query)
|
139
|
+
params["order"] = @order
|
140
|
+
u.query = params.to_query
|
141
|
+
|
142
|
+
u.to_s
|
143
|
+
end
|
144
|
+
|
145
|
+
def guess_locale_based_on_country(country_code)
|
146
|
+
case country_code
|
147
|
+
when /no/i
|
148
|
+
"nb-no"
|
149
|
+
when /fi/i
|
150
|
+
"fi-fi"
|
151
|
+
when /se/i
|
152
|
+
"sv-se"
|
153
|
+
else
|
154
|
+
"sv-se"
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def tax_included_unit_price(item)
|
159
|
+
item.fetch(:unit_price, '').to_i + item.fetch(:tax_amount, '').to_i
|
160
|
+
end
|
161
|
+
|
162
|
+
def tax_rate_for(item)
|
163
|
+
subtotal_price = item.fetch(:unit_price, 0).to_f * item.fetch(:quantity, 0).to_i
|
164
|
+
tax_amount = item.fetch(:tax_amount, 0).to_f
|
165
|
+
|
166
|
+
if subtotal_price > 0
|
167
|
+
tax_rate = tax_amount / subtotal_price
|
168
|
+
tax_rate = tax_rate.round(4)
|
169
|
+
|
170
|
+
percentage_to_two_decimal_precision_whole_number(tax_rate)
|
171
|
+
else
|
172
|
+
0
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def percentage_to_two_decimal_precision_whole_number(percentage)
|
177
|
+
(percentage * 10000).to_i
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
class Notification < OffsitePayments::Notification
|
182
|
+
def initialize(post, options = {})
|
183
|
+
super
|
184
|
+
@shared_secret = @options[:credential2]
|
185
|
+
end
|
186
|
+
|
187
|
+
def complete?
|
188
|
+
status == 'Completed'
|
189
|
+
end
|
190
|
+
|
191
|
+
def item_id
|
192
|
+
order
|
193
|
+
end
|
194
|
+
|
195
|
+
def transaction_id
|
196
|
+
params["reference"]
|
197
|
+
end
|
198
|
+
|
199
|
+
def received_at
|
200
|
+
params["completed_at"]
|
201
|
+
end
|
202
|
+
|
203
|
+
def payer_email
|
204
|
+
params["billing_address"]["email"]
|
205
|
+
end
|
206
|
+
|
207
|
+
def receiver_email
|
208
|
+
params["shipping_address"]["email"]
|
209
|
+
end
|
210
|
+
|
211
|
+
def currency
|
212
|
+
params["purchase_currency"].upcase
|
213
|
+
end
|
214
|
+
|
215
|
+
def gross
|
216
|
+
amount = Float(gross_cents) / 100
|
217
|
+
sprintf("%.2f", amount)
|
218
|
+
end
|
219
|
+
|
220
|
+
def gross_cents
|
221
|
+
params["order_amount"]
|
222
|
+
end
|
223
|
+
|
224
|
+
def status
|
225
|
+
case params['status']
|
226
|
+
when 'checkout_complete'
|
227
|
+
'Completed'
|
228
|
+
else
|
229
|
+
params['status']
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def acknowledge(authcode = nil)
|
234
|
+
Verifier.new(@options[:authorization_header], @raw, @shared_secret).verify
|
235
|
+
end
|
236
|
+
|
237
|
+
private
|
238
|
+
|
239
|
+
def order
|
240
|
+
query = Rack::Utils.parse_nested_query(@options[:query_string])
|
241
|
+
query["order"]
|
242
|
+
end
|
243
|
+
|
244
|
+
def parse(post)
|
245
|
+
@raw = post.to_s
|
246
|
+
@params = JSON.parse(post)
|
247
|
+
end
|
248
|
+
|
249
|
+
class Verifier
|
250
|
+
attr_reader :header, :payload, :digest, :shared_secret
|
251
|
+
def initialize(header, payload, shared_secret)
|
252
|
+
@header, @payload, @shared_secret = header, payload, shared_secret
|
253
|
+
|
254
|
+
@digest = extract_digest
|
255
|
+
end
|
256
|
+
|
257
|
+
def verify
|
258
|
+
digest_matches?
|
259
|
+
end
|
260
|
+
|
261
|
+
private
|
262
|
+
|
263
|
+
def extract_digest
|
264
|
+
match = header.match(/^Klarna (?<digest>.+)$/)
|
265
|
+
match && match[:digest]
|
266
|
+
end
|
267
|
+
|
268
|
+
def digest_matches?
|
269
|
+
Klarna.digest(payload, shared_secret) == digest
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|