better_offsite_payments 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +76 -0
  4. data/lib/offsite_payments.rb +39 -0
  5. data/lib/offsite_payments/action_view_helper.rb +72 -0
  6. data/lib/offsite_payments/helper.rb +120 -0
  7. data/lib/offsite_payments/integrations.rb +14 -0
  8. data/lib/offsite_payments/integrations/a1agregator.rb +245 -0
  9. data/lib/offsite_payments/integrations/authorize_net_sim.rb +580 -0
  10. data/lib/offsite_payments/integrations/bit_pay.rb +150 -0
  11. data/lib/offsite_payments/integrations/bogus.rb +32 -0
  12. data/lib/offsite_payments/integrations/chronopay.rb +283 -0
  13. data/lib/offsite_payments/integrations/citrus.rb +227 -0
  14. data/lib/offsite_payments/integrations/coinbase.rb +172 -0
  15. data/lib/offsite_payments/integrations/direc_pay.rb +332 -0
  16. data/lib/offsite_payments/integrations/directebanking.rb +237 -0
  17. data/lib/offsite_payments/integrations/doku.rb +171 -0
  18. data/lib/offsite_payments/integrations/dotpay.rb +166 -0
  19. data/lib/offsite_payments/integrations/dwolla.rb +160 -0
  20. data/lib/offsite_payments/integrations/e_payment_plans.rb +146 -0
  21. data/lib/offsite_payments/integrations/easy_pay.rb +137 -0
  22. data/lib/offsite_payments/integrations/epay.rb +161 -0
  23. data/lib/offsite_payments/integrations/first_data.rb +133 -0
  24. data/lib/offsite_payments/integrations/gestpay.rb +205 -0
  25. data/lib/offsite_payments/integrations/hi_trust.rb +179 -0
  26. data/lib/offsite_payments/integrations/ipay88.rb +251 -0
  27. data/lib/offsite_payments/integrations/klarna.rb +275 -0
  28. data/lib/offsite_payments/integrations/liqpay.rb +216 -0
  29. data/lib/offsite_payments/integrations/maksuturva.rb +231 -0
  30. data/lib/offsite_payments/integrations/megakassa.rb +184 -0
  31. data/lib/offsite_payments/integrations/mollie.rb +32 -0
  32. data/lib/offsite_payments/integrations/mollie_ideal.rb +194 -0
  33. data/lib/offsite_payments/integrations/mollie_mistercash.rb +143 -0
  34. data/lib/offsite_payments/integrations/molpay.rb +193 -0
  35. data/lib/offsite_payments/integrations/moneybookers.rb +199 -0
  36. data/lib/offsite_payments/integrations/nochex.rb +228 -0
  37. data/lib/offsite_payments/integrations/pag_seguro.rb +268 -0
  38. data/lib/offsite_payments/integrations/paxum.rb +114 -0
  39. data/lib/offsite_payments/integrations/pay_fast.rb +269 -0
  40. data/lib/offsite_payments/integrations/paydollar.rb +142 -0
  41. data/lib/offsite_payments/integrations/payflow_link.rb +194 -0
  42. data/lib/offsite_payments/integrations/paypal.rb +362 -0
  43. data/lib/offsite_payments/integrations/paypal_payments_advanced.rb +23 -0
  44. data/lib/offsite_payments/integrations/paysbuy.rb +71 -0
  45. data/lib/offsite_payments/integrations/payu_in.rb +276 -0
  46. data/lib/offsite_payments/integrations/payu_in_paisa.rb +46 -0
  47. data/lib/offsite_payments/integrations/platron.rb +153 -0
  48. data/lib/offsite_payments/integrations/pxpay.rb +273 -0
  49. data/lib/offsite_payments/integrations/quickpay.rb +232 -0
  50. data/lib/offsite_payments/integrations/rbkmoney.rb +110 -0
  51. data/lib/offsite_payments/integrations/realex_offsite.rb +317 -0
  52. data/lib/offsite_payments/integrations/robokassa.rb +154 -0
  53. data/lib/offsite_payments/integrations/sage_pay_form.rb +431 -0
  54. data/lib/offsite_payments/integrations/two_checkout.rb +329 -0
  55. data/lib/offsite_payments/integrations/universal.rb +190 -0
  56. data/lib/offsite_payments/integrations/valitor.rb +200 -0
  57. data/lib/offsite_payments/integrations/verkkomaksut.rb +143 -0
  58. data/lib/offsite_payments/integrations/web_pay.rb +186 -0
  59. data/lib/offsite_payments/integrations/webmoney.rb +119 -0
  60. data/lib/offsite_payments/integrations/wirecard_checkout_page.rb +359 -0
  61. data/lib/offsite_payments/integrations/world_pay.rb +280 -0
  62. data/lib/offsite_payments/integrations/yandex_money.rb +175 -0
  63. data/lib/offsite_payments/notification.rb +71 -0
  64. data/lib/offsite_payments/return.rb +37 -0
  65. data/lib/offsite_payments/version.rb +3 -0
  66. metadata +297 -0
@@ -0,0 +1,227 @@
1
+ module OffsitePayments
2
+ module Integrations
3
+ module Citrus
4
+ def self.helper(order, account, options = {})
5
+ Helper.new(order, account, options)
6
+ end
7
+
8
+ def self.notification(post, options = {})
9
+ Notification.new(post, options)
10
+ end
11
+
12
+ def self.return(query_string, options = {})
13
+ Return.new(query_string, options)
14
+ end
15
+
16
+ def self.checksum(secret_key, payload_items )
17
+ digest = OpenSSL::Digest.new('sha1')
18
+ OpenSSL::HMAC.hexdigest(digest, secret_key, payload_items)
19
+ end
20
+
21
+ class Helper < OffsitePayments::Helper
22
+ mapping :order, 'merchantTxnId'
23
+ mapping :amount, 'orderAmount'
24
+ mapping :account, 'merchantAccessKey'
25
+ mapping :credential2, 'secret_key'
26
+ mapping :credential3, 'pmt_url'
27
+ mapping :currency, 'currency'
28
+
29
+ mapping :customer, :first_name => 'firstName',:last_name => 'lastName', :email => 'email', :phone => 'mobileNo'
30
+
31
+ mapping :billing_address, :city => 'addressCity', :address1 => 'addressStreet1', :address2 => 'addressStreet2',:state => 'addressState',:zip => 'addressZip', :country => 'addressCountry'
32
+
33
+ mapping :checksum, 'secSignature'
34
+ mapping :return_url, 'returnUrl'
35
+
36
+ SANDBOX_URL = 'https://sandbox.citruspay.com/'.freeze
37
+ STAGING_URL = 'https://stg.citruspay.com/'.freeze
38
+ PRODUCTION_URL = 'https://www.citruspay.com/'.freeze
39
+
40
+ def credential_based_url
41
+ pmt_url = @fields['pmt_url']
42
+ case OffsitePayments.mode
43
+ when :production
44
+ PRODUCTION_URL + pmt_url
45
+ when :test
46
+ SANDBOX_URL + pmt_url
47
+ when :staging
48
+ STAGING_URL + pmt_url
49
+ else
50
+ raise StandardError, "Integration mode set to an invalid value: #{mode}"
51
+ end
52
+ end
53
+
54
+ def initialize(order, account, options = {})
55
+ super
56
+ add_field 'paymentMode', 'NET_BANKING'
57
+ add_field 'reqtime', (Time.now.to_i * 1000).to_s
58
+ end
59
+
60
+ def form_fields
61
+ @fields.merge(mappings[:checksum] => generate_checksum)
62
+ end
63
+
64
+ def generate_checksum
65
+ checksum_fields = @fields["pmt_url"] + @fields["orderAmount"].to_s + @fields["merchantTxnId"] + @fields["currency"]
66
+ Citrus.checksum(@fields["secret_key"], checksum_fields )
67
+ end
68
+ end
69
+
70
+ class Notification < OffsitePayments::Notification
71
+ def initialize(post, options = {})
72
+ super(post, options)
73
+ @secret_key = options[:credential2]
74
+ end
75
+
76
+ def complete?
77
+ status == "Completed" || status == 'Canceled'
78
+ end
79
+
80
+ def status
81
+ @status ||= if checksum_ok?
82
+ if transaction_id.blank?
83
+ 'Invalid'
84
+ else
85
+ case transaction_status.downcase
86
+ when 'success' then 'Completed'
87
+ when 'canceled' then 'Failed'
88
+ end
89
+ end
90
+ else
91
+ 'Tampered'
92
+ end
93
+ end
94
+
95
+ def invoice_ok?( order_id )
96
+ order_id.to_s == invoice.to_s
97
+ end
98
+
99
+ def amount_ok?( order_amount )
100
+ BigDecimal.new( amount ) == order_amount
101
+ end
102
+
103
+ def item_id
104
+ params['TxId']
105
+ end
106
+
107
+ def invoice
108
+ item_id
109
+ end
110
+
111
+ # Status of transaction return from the Citrus. List of possible values:
112
+ # <tt>SUCCESS</tt>::
113
+ # <tt>CANCELED</tt>::
114
+ def transaction_status
115
+ params['TxStatus']
116
+ end
117
+
118
+ def gross
119
+ params['amount']
120
+ end
121
+
122
+ def amount
123
+ gross
124
+ end
125
+
126
+ def transaction_id
127
+ params['pgTxnNo']
128
+ end
129
+
130
+ def issuerrefno
131
+ params['issuerRefNo']
132
+ end
133
+
134
+ def authidcode
135
+ params['authIdCode']
136
+ end
137
+
138
+ def pgrespcode
139
+ params['pgRespCode']
140
+ end
141
+
142
+ def checksum
143
+ params['signature']
144
+ end
145
+
146
+ def paymentmode
147
+ params['paymentMode']
148
+ end
149
+
150
+ def currency
151
+ params['currency']
152
+ end
153
+
154
+ def customer_email
155
+ params['email']
156
+ end
157
+
158
+ def customer_phone
159
+ params['mobileNo']
160
+ end
161
+
162
+ def customer_first_name
163
+ params['firstName']
164
+ end
165
+
166
+ def customer_last_name
167
+ params['lastName']
168
+ end
169
+
170
+ def customer_address
171
+ { :address1 => params['addressStreet1'], :address2 => params['addressStreet2'],
172
+ :city => params['addressCity'], :state => params['addressState'],
173
+ :country => params['addressCountry'], :zip => params['addressZip'] }
174
+ end
175
+
176
+ def message
177
+ @message || params['TxMsg']
178
+ end
179
+
180
+ def acknowledge(authcode = nil)
181
+ checksum_ok?
182
+ end
183
+
184
+ def checksum_ok?
185
+ fields = [invoice, transaction_status, amount.to_s, transaction_id, issuerrefno, authidcode, customer_first_name, customer_last_name, pgrespcode, customer_address[:zip]].join
186
+
187
+ unless Citrus.checksum(@secret_key, fields ) == checksum
188
+ @message = 'checksum mismatch...'
189
+ return false
190
+ end
191
+ true
192
+ end
193
+ end
194
+
195
+ class Return < OffsitePayments::Return
196
+ def initialize(query_string, options = {})
197
+ super
198
+ @notification = Notification.new(query_string, options)
199
+ end
200
+
201
+ def transaction_id
202
+ @notification.transaction_id
203
+ end
204
+
205
+ def status( order_id, order_amount )
206
+ if @notification.invoice_ok?( order_id ) && @notification.amount_ok?( BigDecimal.new(order_amount) )
207
+ @notification.status
208
+ else
209
+ 'Mismatch'
210
+ end
211
+ end
212
+
213
+ def success?
214
+ status( @params['TxId'], @params['amount'] ) == 'Completed'
215
+ end
216
+
217
+ def message
218
+ @notification.message
219
+ end
220
+
221
+ def cancelled?
222
+ @notification.status == 'Failed'
223
+ end
224
+ end
225
+ end
226
+ end
227
+ end
@@ -0,0 +1,172 @@
1
+ module OffsitePayments #:nodoc:
2
+ module Integrations #:nodoc:
3
+ module Coinbase
4
+ mattr_accessor :service_url
5
+ self.service_url = 'https://www.coinbase.com/checkouts/redirect'
6
+
7
+ mattr_accessor :buttoncreate_url
8
+ self.buttoncreate_url = 'https://api.coinbase.com/v1/buttons'
9
+
10
+ mattr_accessor :notification_confirmation_url
11
+ self.notification_confirmation_url = 'https://api.coinbase.com/v1/orders/%s'
12
+
13
+ # options should be { credential1: "your API key", credential2: "your API secret" }
14
+ def self.notification(post, options = {})
15
+ Notification.new(post, options)
16
+ end
17
+
18
+ def self.return(query_string, options = {})
19
+ Return.new(query_string, options)
20
+ end
21
+
22
+ class Helper < OffsitePayments::Helper
23
+ # account should be a Coinbase API key; see https://coinbase.com/account/integrations
24
+ # options[:credential2] should be the corresponding API secret
25
+ def initialize(order_id, account, options)
26
+ super
27
+
28
+ @order = order_id
29
+ @account = account
30
+ @options = options
31
+ @options[:credential1] ||= ''
32
+ @options[:credential2] ||= ''
33
+ end
34
+
35
+ mapping :notify_url, 'notify_url'
36
+ mapping :return_url, 'return_url'
37
+ mapping :cancel_return_url, 'cancel_return_url'
38
+
39
+ def form_fields
40
+ uri = URI.parse(Coinbase.buttoncreate_url)
41
+
42
+ request_body = {
43
+ 'button[auto_redirect]' => true,
44
+ 'button[name]' => @options[:description] || "Your Order",
45
+ 'button[price_string]' => @options[:amount],
46
+ 'button[price_currency_iso]' => @options[:currency],
47
+ 'button[custom]' => @order,
48
+ 'button[callback_url]' => @fields['notify_url'],
49
+ 'button[success_url]' => @fields['return_url'],
50
+ 'button[cancel_url]' => @fields['cancel_return_url'],
51
+ 'api_key' => @account
52
+ }.to_query
53
+
54
+ data = Coinbase.do_request(uri, @account, @options[:credential2], request_body)
55
+ json = JSON.parse(data)
56
+
57
+ raise ActionViewHelperError, "Error occured while contacting gateway : #{json['error']}" if json['error']
58
+
59
+ {'id' => json['button']['code']}
60
+ rescue JSON::ParserError
61
+ raise ActionViewHelperError, 'Invalid response from gateway. Please try again.'
62
+ end
63
+ end
64
+
65
+ class Notification < OffsitePayments::Notification
66
+
67
+ def complete?
68
+ status == "Completed"
69
+ end
70
+
71
+ def item_id
72
+ params['custom']
73
+ end
74
+
75
+ def transaction_id
76
+ params['id']
77
+ end
78
+
79
+ def received_at
80
+ Time.iso8601(params['created_at']).to_time.to_i
81
+ end
82
+
83
+ def gross
84
+ if params['total_original'].present?
85
+ "%.2f" % (params['total_original']['cents'].to_f / 100)
86
+ else
87
+ "%.2f" % (params['total_native']['cents'].to_f / 100)
88
+ end
89
+ end
90
+
91
+ def currency
92
+ params['total_native']['currency_iso']
93
+ end
94
+
95
+ def status
96
+ case params['status']
97
+ when "completed"
98
+ "Completed"
99
+ else
100
+ "Failed"
101
+ end
102
+ end
103
+
104
+ # Acknowledge the transaction to Coinbase. This method has to be called after a new
105
+ # apc arrives. Coinbase will verify that all the information we received are correct
106
+ # and will return a ok or a fail.
107
+ def acknowledge(authcode = {})
108
+ uri = URI.parse(Coinbase.notification_confirmation_url % transaction_id)
109
+
110
+ response = Coinbase.do_request(uri, @options[:credential1], @options[:credential2])
111
+ return false if response.nil?
112
+
113
+ posted_order = @params
114
+ parse(response)
115
+
116
+ return false unless @params
117
+ %w(id custom total_native status).all? { |param| posted_order[param] == @params[param] }
118
+ end
119
+
120
+ private
121
+
122
+ def parse(post)
123
+ @raw = post.to_s
124
+ @params = JSON.parse(post)['order']
125
+ rescue JSON::ParserError
126
+ @params = {}
127
+ end
128
+ end
129
+
130
+ class Return < OffsitePayments::Return
131
+ def initialize(query_string, options = {})
132
+ super
133
+ @notification = Notification.new(@params.to_json, options)
134
+ end
135
+
136
+ def parse(query_string)
137
+ parsed_hash = Rack::Utils.parse_nested_query(query_string)
138
+
139
+ if native_cents = parsed_hash['order'] && parsed_hash['order']['total_native'] && parsed_hash['order']['total_native']['cents']
140
+ parsed_hash['order']['total_native']['cents'] = native_cents.to_i
141
+ end
142
+
143
+ parsed_hash
144
+ end
145
+ end
146
+
147
+ protected
148
+
149
+ def self.do_request(uri, api_key, api_secret, post_body = nil)
150
+ nonce = (Time.now.to_f * 1e6).to_i
151
+ hmac_message = nonce.to_s + uri.to_s
152
+
153
+ if post_body
154
+ request = Net::HTTP::Post.new(uri.request_uri)
155
+ request.body = post_body
156
+ hmac_message = hmac_message + request.body
157
+ else
158
+ request = Net::HTTP::Get.new(uri.path)
159
+ end
160
+
161
+ http = Net::HTTP.new(uri.host, uri.port)
162
+ http.use_ssl = true
163
+
164
+ request['ACCESS_KEY'] = api_key
165
+ request['ACCESS_SIGNATURE'] = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), api_secret, hmac_message)
166
+ request['ACCESS_NONCE'] = nonce.to_s
167
+
168
+ http.request(request).body
169
+ end
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,332 @@
1
+ module OffsitePayments #:nodoc:
2
+ module Integrations #:nodoc:
3
+ module DirecPay
4
+ mattr_accessor :production_url, :test_url
5
+
6
+ self.production_url = "https://www.timesofmoney.com/direcpay/secure/dpMerchantTransaction.jsp"
7
+ self.test_url = "https://test.direcpay.com/direcpay/secure/dpMerchantTransaction.jsp"
8
+
9
+ def self.service_url
10
+ mode = OffsitePayments.mode
11
+ case mode
12
+ when :production
13
+ self.production_url
14
+ when :test
15
+ self.test_url
16
+ else
17
+ raise StandardError, "Integration mode set to an invalid value: #{mode}"
18
+ end
19
+ end
20
+
21
+ def self.notification(post, options = {})
22
+ Notification.new(post)
23
+ end
24
+
25
+ def self.return(query_string, options = {})
26
+ Return.new(query_string, options)
27
+ end
28
+
29
+ def self.request_status_update(mid, transaction_id, notification_url)
30
+ Status.new(mid).update(transaction_id, notification_url)
31
+ end
32
+
33
+ class Helper < OffsitePayments::Helper
34
+ mapping :account, 'MID'
35
+ mapping :order, 'Merchant Order No'
36
+ mapping :amount, 'Amount'
37
+ mapping :currency, 'Currency'
38
+ mapping :country, 'Country'
39
+
40
+ mapping :billing_address, :city => 'custCity',
41
+ :address1 => 'custAddress',
42
+ :state => 'custState',
43
+ :zip => 'custPinCode',
44
+ :country => 'custCountry',
45
+ :phone => 'custMobileNo'
46
+
47
+ mapping :shipping_address, :name => 'deliveryName',
48
+ :city => 'deliveryCity',
49
+ :address1 => 'deliveryAddress',
50
+ :state => 'deliveryState',
51
+ :zip => 'deliveryPinCode',
52
+ :country => 'deliveryCountry',
53
+ :phone => 'deliveryMobileNo'
54
+
55
+ mapping :customer, :name => 'custName',
56
+ :email => 'custEmailId'
57
+
58
+ mapping :description, 'otherNotes'
59
+ mapping :edit_allowed, 'editAllowed'
60
+
61
+ mapping :return_url, 'Success URL'
62
+ mapping :failure_url, 'Failure URL'
63
+
64
+ mapping :operating_mode, 'Operating Mode'
65
+ mapping :other_details, 'Other Details'
66
+ mapping :collaborator, 'Collaborator'
67
+
68
+ OPERATING_MODE = 'DOM'
69
+ COUNTRY = 'IND'
70
+ CURRENCY = 'INR'
71
+ OTHER_DETAILS = 'NULL'
72
+ EDIT_ALLOWED = 'Y'
73
+
74
+ PHONE_CODES = {
75
+ 'IN' => '91',
76
+ 'US' => '01',
77
+ 'CA' => '01'
78
+ }
79
+
80
+ ENCODED_PARAMS = [ :account, :operating_mode, :country, :currency, :amount, :order, :other_details, :return_url, :failure_url, :collaborator ]
81
+
82
+ def initialize(order, account, options = {})
83
+ super
84
+ collaborator = OffsitePayments.mode == :test || options[:test] ? 'TOML' : 'DirecPay'
85
+ add_field(mappings[:collaborator], collaborator)
86
+ add_field(mappings[:country], 'IND')
87
+ add_field(mappings[:operating_mode], OPERATING_MODE)
88
+ add_field(mappings[:other_details], OTHER_DETAILS)
89
+ add_field(mappings[:edit_allowed], EDIT_ALLOWED)
90
+ end
91
+
92
+
93
+ def customer(params = {})
94
+ add_field(mappings[:customer][:name], full_name(params))
95
+ add_field(mappings[:customer][:email], params[:email])
96
+ end
97
+
98
+ # Need to format the amount to have 2 decimal places
99
+ def amount=(money)
100
+ cents = money.respond_to?(:cents) ? money.cents : money
101
+ raise ArgumentError, "amount must be a Money object or an integer" if money.is_a?(String)
102
+ raise ActionViewHelperError, "amount must be greater than $0.00" if cents.to_i <= 0
103
+
104
+ add_field(mappings[:amount], sprintf("%.2f", cents.to_f/100))
105
+ end
106
+
107
+ def shipping_address(params = {})
108
+ super(update_address(:shipping_address, params))
109
+ end
110
+
111
+ def billing_address(params = {})
112
+ super(update_address(:billing_address, params))
113
+ end
114
+
115
+ def form_fields
116
+ add_failure_url
117
+ add_request_parameters
118
+
119
+ unencoded_parameters
120
+ end
121
+
122
+ private
123
+
124
+ def add_request_parameters
125
+ params = ENCODED_PARAMS.map{ |param| fields[mappings[param]] }
126
+ encoded = encode_value(params.join('|'))
127
+
128
+ add_field('requestparameter', encoded)
129
+ end
130
+
131
+ def unencoded_parameters
132
+ params = fields.dup
133
+ # remove all encoded params from exported fields
134
+ ENCODED_PARAMS.each{ |param| params.delete(mappings[param]) }
135
+ # remove all special characters from each field value
136
+ params = params.collect{|name, value| [name, remove_special_characters(value)] }
137
+ Hash[params]
138
+ end
139
+
140
+ def add_failure_url
141
+ if fields[mappings[:failure_url]].nil?
142
+ add_field(mappings[:failure_url], fields[mappings[:return_url]])
143
+ end
144
+ end
145
+
146
+ def update_address(address_type, params)
147
+ params = params.dup
148
+ address = params[:address1]
149
+ address = "#{address} #{params[:address2]}" if params[:address2].present?
150
+ address = "#{params[:company]} #{address}" if params[:company].present?
151
+ params[:address1] = address
152
+
153
+ params[:phone] = normalize_phone_number(params[:phone])
154
+ add_land_line_phone_for(address_type, params)
155
+
156
+ if address_type == :shipping_address
157
+ shipping_name = full_name(params) || fields[mappings[:customer][:name]]
158
+ add_field(mappings[:shipping_address][:name], shipping_name)
159
+ end
160
+ params
161
+ end
162
+
163
+ # Split a single phone number into the country code, area code and local number as best as possible
164
+ def add_land_line_phone_for(address_type, params)
165
+ address_field = address_type == :billing_address ? 'custPhoneNo' : 'deliveryPhNo'
166
+
167
+ if params.has_key?(:phone2)
168
+ phone = normalize_phone_number(params[:phone2])
169
+ phone_country_code, phone_area_code, phone_number = nil
170
+
171
+ if params[:country] == 'IN' && phone =~ /(91)? *(\d{3}) *(\d{4,})$/
172
+ phone_country_code, phone_area_code, phone_number = $1, $2, $3
173
+ else
174
+ numbers = phone.split(' ')
175
+ case numbers.size
176
+ when 3
177
+ phone_country_code, phone_area_code, phone_number = numbers
178
+ when 2
179
+ phone_area_code, phone_number = numbers
180
+ else
181
+ phone =~ /(\d{3})(\d+)$/
182
+ phone_area_code, phone_number = $1, $2
183
+ end
184
+ end
185
+
186
+ add_field("#{address_field}1", phone_country_code || phone_code_for_country(params[:country]) || '91')
187
+ add_field("#{address_field}2", phone_area_code)
188
+ add_field("#{address_field}3", phone_number)
189
+ end
190
+ end
191
+
192
+ def normalize_phone_number(phone)
193
+ phone.gsub(/[^\d ]+/, '') if phone
194
+ end
195
+
196
+ # Special characters are NOT allowed while posting transaction parameters on DirecPay system
197
+ def remove_special_characters(string)
198
+ string.gsub(/[~"'&#%]/, '-')
199
+ end
200
+
201
+ def encode_value(value)
202
+ encoded = Base64.strict_encode64(value)
203
+ string_to_encode = encoded[0, 1] + "T" + encoded[1, encoded.length]
204
+ Base64.strict_encode64(string_to_encode)
205
+ end
206
+
207
+ def decode_value(value)
208
+ decoded = Base64.decode64(value)
209
+ string_to_decode = decoded[0, 1] + decoded[2, decoded.length]
210
+ Base64.decode64(string_to_decode)
211
+ end
212
+
213
+ def phone_code_for_country(country)
214
+ PHONE_CODES[country]
215
+ end
216
+
217
+ def full_name(params)
218
+ return if params[:name].blank? && params[:first_name].blank? && params[:last_name].blank?
219
+
220
+ params[:name] || "#{params[:first_name]} #{params[:last_name]}"
221
+ end
222
+ end
223
+
224
+ class Notification < OffsitePayments::Notification
225
+ RESPONSE_PARAMS = ['DirecPay Reference ID', 'Flag', 'Country', 'Currency', 'Other Details', 'Merchant Order No', 'Amount']
226
+
227
+ def acknowledge(authcode = nil)
228
+ true
229
+ end
230
+
231
+ def complete?
232
+ status == 'Completed' || status == 'Pending'
233
+ end
234
+
235
+ def status
236
+ case params['Flag']
237
+ when 'SUCCESS'
238
+ 'Completed'
239
+ when 'PENDING'
240
+ 'Pending'
241
+ when 'FAIL'
242
+ 'Failed'
243
+ else
244
+ 'Error'
245
+ end
246
+ end
247
+
248
+ def item_id
249
+ params['Merchant Order No']
250
+ end
251
+
252
+ def transaction_id
253
+ params['DirecPay Reference ID']
254
+ end
255
+
256
+ # the money amount we received in X.2 decimal
257
+ def gross
258
+ params['Amount']
259
+ end
260
+
261
+ def currency
262
+ params['Currency']
263
+ end
264
+
265
+ def country
266
+ params['Country']
267
+ end
268
+
269
+ def other_details
270
+ params['Other Details']
271
+ end
272
+
273
+ def test?
274
+ false
275
+ end
276
+
277
+ # Take the posted data and move the relevant data into a hash
278
+ def parse(post)
279
+ super
280
+
281
+ values = params['responseparams'].to_s.split('|')
282
+ response_params = values.size == 3 ? ['DirecPay Reference ID', 'Flag', 'Error message'] : RESPONSE_PARAMS
283
+ response_params.each_with_index do |name, index|
284
+ params[name] = values[index]
285
+ end
286
+ params
287
+ end
288
+ end
289
+
290
+ class Return < OffsitePayments::Return
291
+ def initialize(post_data, options = {})
292
+ @notification = Notification.new(post_data, options)
293
+ end
294
+
295
+ def success?
296
+ notification.complete?
297
+ end
298
+
299
+ def message
300
+ notification.status
301
+ end
302
+ end
303
+
304
+ class Status
305
+ include ActiveUtils::PostsData
306
+
307
+ STATUS_TEST_URL = 'https://test.direcpay.com/direcpay/secure/dpMerchantTransaction.jsp'
308
+ STATUS_LIVE_URL = 'https://www.timesofmoney.com/direcpay/secure/dpPullMerchAtrnDtls.jsp'
309
+
310
+ attr_reader :account, :options
311
+
312
+ def initialize(account, options = {})
313
+ @account, @options = account, options
314
+ end
315
+
316
+ # Use this method to manually request a status update to the provided notification_url
317
+ def update(authorization, notification_url)
318
+ url = test? ? STATUS_TEST_URL : STATUS_LIVE_URL
319
+ parameters = [ authorization, account, notification_url ]
320
+ data = ActiveUtils::PostData.new
321
+ data[:requestparams] = parameters.join('|')
322
+
323
+ response = ssl_get("#{url}?#{data.to_post_data}")
324
+ end
325
+
326
+ def test?
327
+ OffsitePayments.mode == :test || options[:test]
328
+ end
329
+ end
330
+ end
331
+ end
332
+ end