prismpay 0.0.8 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,620 @@
1
+ module PrismPay
2
+
3
+ class PrismPay
4
+ # this class will manage the connection to the gateway and handle
5
+ # transactions
6
+
7
+ # WSDL = "https://trans.myprismpay.com/MPWeb/services/TransactionService?wsdl"
8
+ WSDL = File.expand_path("../TransactionService.xml", __FILE__)
9
+
10
+ ACH_CHECK_ACCT_TYPE = {"checking" => 1, "savings" => 2}
11
+ ACH_SEC_CODES = %w(POP, ARC, TEL, PPD, ICL, RCK, BOC, CCDN)
12
+
13
+ attr_accessor :acctid, :password
14
+ attr_reader :client
15
+
16
+ def initialize(options = {})
17
+ merchant_info = options
18
+ merchant_info.merge!({:login => 'TEST0'}) unless merchant_info[:login]
19
+
20
+ if merchant_info.respond_to?("has_key?")
21
+ @acctid = merchant_info[:login] if merchant_info.has_key?(:login)
22
+ @password = merchant_info[:password] if merchant_info.has_key?(:password)
23
+ end
24
+
25
+ @client = Savon::Client.new(WSDL) # initialize savon client
26
+
27
+ end
28
+
29
+
30
+ ############################################################
31
+ # CreditCard SOAP methods:
32
+ # ###########################################################
33
+ # TODO: the profile methods should be refactored to be either for
34
+ # credit cards or for ACH transactions
35
+ ############################################################
36
+
37
+ def profile_sale(amount, profile_id, last_four, options = {})
38
+ # response = @client.request :process_profile_sale do
39
+ response = @client.request 'processProfileSale' do
40
+ http.auth.ssl.verify_mode = :none
41
+ soap.body &build_profile_sale(amount, profile_id, last_four, options)
42
+ end
43
+ PrismCreditResponse.new(response)
44
+ end
45
+
46
+
47
+ def profile_retrieve(options = {})
48
+ # process a profile retrieve request
49
+ # response = @client.request :process_profile_retrieve do
50
+ response = @client.request 'processProfileRetrieve' do
51
+ http.auth.ssl.verify_mode = :none
52
+ soap.body &build_profile_retrieve(options)
53
+ end
54
+ end
55
+
56
+
57
+ def cc_purchase(amount, creditcard, options ={})
58
+ # process a credit card sale and right now return the savon response
59
+ # The savon response needs to be mapped back into the proper response
60
+ # fields
61
+
62
+ # need to merge the gateway instance options with the options
63
+
64
+ # response = @client.request :process_cc_sale do
65
+ response = @client.request 'processCCSale' do
66
+ http.auth.ssl.verify_mode = :none
67
+ soap.body &build_cc_sale_auth(amount, creditcard, options)
68
+ end
69
+
70
+ PrismCreditResponse.new(response)
71
+
72
+ end
73
+
74
+
75
+ def cc_authorize(amount, creditcard, options = {})
76
+ # reserve funds for future captures
77
+ # response = @client.request :process_cc_auth do
78
+ response = @client.request 'processCCAuth' do
79
+ http.auth.ssl.verify_mode = :none
80
+ soap.body &build_cc_sale_auth(amount, creditcard, options)
81
+ end
82
+
83
+ PrismCreditResponse.new(response)
84
+ end
85
+
86
+
87
+ def cc_capture(amount, authorization, pp_txn_id, options = {})
88
+ # Captures reservered funds from previous auths
89
+ # need to put some validation into these methods before
90
+ # making the call to the build methods
91
+
92
+ # response = @client.request :process_cc_post do
93
+ response = @client.request 'processCCPost' do
94
+ http.auth.ssl.verify_mode = :none
95
+ soap.body &build_cc_capture(amount, authorization, pp_txn_id, options)
96
+ end
97
+
98
+ PrismCreditResponse.new(response)
99
+ end
100
+
101
+
102
+ def cc_void(amount, identification, pp_txn_id, options = {})
103
+ # voids previous transactions
104
+ # response = @client.request :process_cc_void do
105
+ response = @client.request 'processCCVoid' do
106
+ http.auth.ssl.verify_mode = :none
107
+ soap.body &build_cc_void(amount, identification, pp_txn_id, options)
108
+ end
109
+
110
+ PrismCreditResponse.new(response)
111
+ end
112
+
113
+
114
+ def credit(amount, identification, pp_txn_id, options = {})
115
+ # applies credit back against previous transaction
116
+ # response = @client.request :process_credit do
117
+ response = @client.request 'processCredit' do
118
+ http.auth.ssl.verify_mode = :none
119
+ soap.body &build_credit(amount, identification, pp_txn_id, options)
120
+ end
121
+
122
+ PrismCreditResponse.new(response)
123
+ end
124
+
125
+ ############################################################
126
+ # ACH Methods
127
+ ############################################################
128
+ # These should be for normal ACH transactions ie: transactions
129
+ # coming from a client into a prismpay vendor.
130
+ ############################################################
131
+
132
+ def ach_sale(amount, bank_account, options = {})
133
+ # response = @client.request :process_ext_ach_sale do
134
+ response = @client.request 'processACHSale' do
135
+ http.auth.ssl.verify_mode = :none
136
+ soap.body &build_ext_ach_sale_disburse(amount, bank_account, options)
137
+ end
138
+
139
+ PrismCreditResponse.new(response)
140
+ end
141
+
142
+ # def ach_credit(amount, auth, pp_txn_id, options = {})
143
+ # response = @client.request :process_ach_credit do
144
+ # http.auth.ssl.verify_mode = :none
145
+ # soap.body &build_ach_credit()
146
+ # end
147
+
148
+ # PrismCreditResponse.new(response)
149
+ # end
150
+
151
+
152
+ ############################################################
153
+ # ACH Ext Check Sale
154
+ ############################################################
155
+ # These will be used for checks coming in which will have
156
+ # disbursments to a third party as well
157
+ ############################################################
158
+
159
+ def ext_ach_sale(amount, bank_account, options = {})
160
+ # response = @client.request :process_ext_ach_sale do
161
+ response = @client.request 'processExtACHSale' do
162
+ http.auth.ssl.verify_mode = :none
163
+ soap.body &build_ext_ach_sale_disburse(amount, bank_account, options)
164
+ end
165
+
166
+ PrismCreditResponse.new(response)
167
+ end
168
+
169
+ ############################################################
170
+ # ext_ach_void doesn't work in generic testing accounts
171
+ ############################################################
172
+ def ach_void(identification, pp_txn_id, options = {})
173
+ # response = @client.request :process_ext_ach_void do
174
+ response = @client.request 'processVoid' do
175
+ http.auth.ssl.verify_mode = :none
176
+ soap.body &build_ext_ach_refund_void(nil, identification,
177
+ pp_txn_id, options)
178
+ end
179
+
180
+ PrismCreditResponse.new(response)
181
+ end
182
+
183
+ def ach_refund(amount, identification, pp_txn_id, options = {})
184
+ # response = @client.request :process_ext_ach_credit do
185
+ response = @client.request 'processACHCreditRequest' do
186
+ http.auth.ssl.verify_mode = :none
187
+ soap.body &build_ext_ach_refund_void(amount, identification,
188
+ pp_txn_id, options)
189
+ end
190
+
191
+ PrismCreditResponse.new(response)
192
+ end
193
+
194
+ def ext_ach_consumer_disbursement(amount, bank_account, options = {})
195
+ # response = @client.request :process_ext_ach_consumer_disbursement do
196
+ response = @client.request 'processExtACHConsumerDisbursement' do
197
+ http.auth.ssl.verify_mode = :none
198
+ soap.body &build_ext_ach_sale_disburse(amount, bank_account, options)
199
+ end
200
+
201
+ PrismCreditResponse.new(response)
202
+ end
203
+
204
+
205
+
206
+ # helper methods
207
+ private
208
+
209
+ def build_address(addr, type= "bill")
210
+ # receives a hash that contains the keys for active_merchant
211
+ # address and type which should be 'bill' or 'ship'
212
+ # returns a str to be eval and included in xml builder block
213
+
214
+ if type != "bill" && type != "ship"
215
+ type = "bill"
216
+ end
217
+
218
+ retstr =
219
+ "xml.#{type}address('xsi:type' => 'urn:address'){
220
+ xml.addr1 '#{addr[:address1]}'
221
+ xml.addr2 '#{addr[:address2]}'
222
+ xml.city '#{addr[:city]}'
223
+ xml.state '#{addr[:state]}'
224
+ xml.zip '#{addr[:zip]}'
225
+ xml.country '#{addr[:country]}'
226
+ }"
227
+ end
228
+
229
+ def build_profile_retrieve(options = {})
230
+ xml_block = Proc.new { |xml|
231
+ xml.miscprocess("xsi:type" => "urn:ProfileRetrieve"){
232
+ xml.acctid @acctid
233
+ xml.merchantpin @password if @password
234
+ xml.subid options[:subid] if options[:subid]
235
+ xml.last4digits options[:last_four]
236
+ xml.userprofileid options[:profileid]
237
+ xml.merchantpin options[:merchantpin] if options[:merchantpin]
238
+ xml.ipaddress
239
+ }
240
+ }
241
+
242
+ return xml_block
243
+ end
244
+
245
+ def build_credit(amount, id, pp_txn_id, options)
246
+ xml_block = Proc.new { |xml|
247
+ xml.miscprocess("xsi:type" => "urn:VoidCreditPost"){
248
+ xml.acctid @acctid
249
+ xml.merchantpin @password if @password
250
+ xml.amount amount
251
+ xml.orderid pp_txn_id
252
+ xml.historyid id
253
+ xml.ipaddress
254
+ }
255
+ }
256
+
257
+ return xml_block
258
+ end
259
+
260
+ def build_cc_void(amount, auth, pp_txn_id, options)
261
+ # needs to have orderid and amount in options
262
+ xml_block = Proc.new {|xml|
263
+ xml.miscprocess("xsi:type" => "urn:VoidCreditPost"){
264
+ xml.acctid @acctid
265
+ xml.merchantpin @password if @password
266
+ xml.amount amount
267
+ xml.orderid pp_txn_id
268
+ # xml.customizedfields{
269
+ # xml.custom1
270
+ # xml.custom2
271
+ # xml.custom3
272
+ # xml.custom4
273
+ # xml.custom5
274
+ # xml.custom6
275
+ # }
276
+ xml.historyid auth
277
+ xml.ipaddress
278
+ }
279
+ }
280
+
281
+ return xml_block
282
+ end
283
+
284
+ def build_cc_capture(amount, auth, pp_txn_id, options)
285
+ # as of now auth is historyid and we need :orderid set in options
286
+ xml_block = Proc.new {|xml|
287
+ xml.miscprocess("xsi:type" => "urn:VoidCreditPost"){
288
+ xml.acctid @acctid
289
+ xml.merchantpin @password if @password
290
+ xml.amount amount
291
+ xml.orderid pp_txn_id
292
+ xml.historyid auth
293
+ xml.merchantordernumber auth
294
+ xml.merchantpin auth
295
+ xml.ipaddress
296
+ }
297
+ }
298
+ return xml_block
299
+ end
300
+
301
+ def build_cc_sale_auth(amount, credit_card, options)
302
+ # return a proc object to be used as a block for builder
303
+ # passed to response.body {|xml| my xml block}
304
+
305
+ missing_fields_for_options = {
306
+ :acctid => '',
307
+ :merchantpin => '',
308
+ :subid => ''
309
+ }
310
+
311
+ # to map the active_merchant option keys to prismpay
312
+ active_merchant_credit_card = {
313
+ :first_name => '',
314
+ :last_name => '',
315
+ :month => '',
316
+ :number => '',
317
+ :type => '',
318
+ :verification_value => '',
319
+ :year => ''
320
+ }
321
+
322
+ active_merchant_option_map = {
323
+ :order_id => :merchantordernumber,
324
+ :ip => :ipaddress,
325
+ :customer => '', # customer info
326
+ :invoice => '', # invoice
327
+ :merchant => '', # name of merchant offering the product
328
+ :description => '', # A description of the transaction
329
+ :email => :email, # The email address of the customer
330
+ :currency => :currencycode,
331
+ :address => '', # if this is set it is both billing and shipping
332
+ :billing_address => {
333
+ :name => '',
334
+ :company => '',
335
+ :address1 => '',
336
+ :address2 => '',
337
+ :city => '',
338
+ :state => '',
339
+ :country => '',
340
+ :zip => '',
341
+ :phone => ''
342
+ },
343
+ :shipping_address => {
344
+ :name => '',
345
+ :company => '',
346
+ :address1 => '',
347
+ :address2 => '',
348
+ :city => '',
349
+ :state => '',
350
+ :country => '',
351
+ :zip => '',
352
+ :phone => ''
353
+ }
354
+ }
355
+
356
+ if options.has_key?(:address)
357
+ bill_address = ship_address = options[:address]
358
+ else
359
+ # assigns nil to variables if keys aren't present
360
+ bill_address = options[:billing_address]
361
+ ship_address = options[:shipping_address]
362
+ end
363
+
364
+ xml_block = Proc.new{ |xml|
365
+ xml.ccinfo("xsi:type" => "urn:CreditCardInfo") {
366
+ xml.acctid @acctid
367
+ xml.merchantpin @password if @password
368
+ # xml.merchantpin options[:password] if options.has_key?(:password)
369
+ # xml.subid "xsi:nil" => "true"
370
+ xml.ccname "#{credit_card.first_name} #{credit_card.last_name}"
371
+ # xml.swipedata "xsi:nil" => "true"
372
+ # xml.cardpresent "xsi;nil" => "true"
373
+ # xml.cardreaderpresent "xsi:nil" => "true"
374
+ # xml.voiceauth "xsi:nil" => "true"
375
+ # xml.track1 "xsi:nil" => "true"
376
+ # xml.track2 "xsi:nil" => "true"
377
+ xml.ccnum credit_card.number
378
+ xml.cctype credit_card.type
379
+ xml.expmon credit_card.month
380
+ xml.expyear credit_card.year
381
+ xml.cvv2 credit_card.verification_value
382
+ xml.amount amount
383
+ xml.merchantordernumber options[:order_id] if options.has_key?(:order_id) # or invoice?
384
+ # xml.companyname # says its our companyname
385
+ eval(build_address(bill_address)) if bill_address
386
+ eval(build_address(ship_address, "ship")) if ship_address
387
+ xml.email options[:email] if options.has_key?(:email)
388
+ # xml.dlnum
389
+ # xml.ssnum
390
+ xml.phone bill_address[:phone] if bill_address
391
+ # xml.dobday
392
+ # xml.dobmonth
393
+ # xml.dobyear
394
+ # xml.memo
395
+ # xml.customizedemail("xsi:type" => "urn:customEmail"){ #method
396
+ # xml.emailto "vpat@comcast.net"
397
+ # xml.emailfrom "null@atsbank.com"
398
+ # xml.emailsubject "Transaction Service Test"
399
+ # xml.emailtext "This is just a test"
400
+ # }
401
+ # xml.recurring("xsi:type" => "urn:Recur") { #nees method
402
+ # xml.create 0
403
+ # xml.billingcycle 0
404
+ # xml.billingmax 0
405
+ # xml.start 0
406
+ # xml.amount 0
407
+ # }
408
+ xml.ipaddress options[:ip] # req field ... nil if !(exists?)
409
+ # xml.accttype ---> #have no clue
410
+ # xml.merchantpin ----> #believe this is password
411
+ # xml.currencycode
412
+ # xml.industrycode ----> # no clue
413
+ # xml.dynamicdescriptor ---> carries onto receipt for VITAL auths
414
+ # xml.profileactiontype # no clue
415
+ # xml.manualrecurring #number 1 if manual recurring
416
+ }
417
+ }
418
+
419
+ return xml_block
420
+ end
421
+
422
+
423
+ def build_profile_sale(amount, profile_id, last_four, options)
424
+ # as of now auth is historyid and we need :orderid set in options
425
+
426
+ xml_block = Proc.new {|xml|
427
+ xml.miscprocess("xsi:type" => "urn:MPTransProcess"){
428
+ xml.acctid @acctid
429
+ xml.merchantpin @password if @password
430
+ xml.last4digits last_four
431
+ xml.userprofileid profile_id
432
+ xml.amount amount
433
+ xml.ipaddress
434
+ }
435
+ }
436
+ return xml_block
437
+ end
438
+
439
+
440
+ def build_ext_ach_sale_disburse(amount, bank_account, options)
441
+ # should probably pass in the companyname under options
442
+ acct_type = "#{bank_account.account_holder_type} " +
443
+ "#{bank_account.account_type}"
444
+
445
+ xml_block = Proc.new { |xml|
446
+ xml.ckinfo("xsi:type" => "urn:ACHInfo"){
447
+ xml.acctid @acctid
448
+ xml.merchantpin(@password) if @password
449
+ xml.ckname bank_account.name
450
+ xml.firstname bank_account.first_name
451
+ xml.lastname bank_account.last_name
452
+ xml.ckaba bank_account.routing_number
453
+ xml.ckacct bank_account.account_number
454
+ xml.ckno(bank_account.cknumber) if bank_account.number
455
+ xml.amount amount
456
+ if bank_account.account_holder_type =~ /business/i
457
+ xml.cktype('CCD')
458
+ else
459
+ xml.cktype('PPD')
460
+ end
461
+ xml.ckaccttypedesc acct_type
462
+ xml.companyname options[:companyname]
463
+ xml.ipaddress
464
+ }
465
+ }
466
+
467
+ return xml_block
468
+ end
469
+
470
+ def build_ext_ach_refund_void(amount, auth, pp_txn_id, options)
471
+ # should probably pass in the companyname under options
472
+ Rails.logger.info("######DEBUG############}")
473
+ Rails.logger.info("Order ID: #{pp_txn_id} - History ID: #{auth} - ACCID: #{@acctid} - Amout: #{amount}")
474
+ xml_block = Proc.new { |xml|
475
+ xml.ckinfo("xsi:type" => "urn:ACHInfo"){
476
+ xml.acctid @acctid
477
+ xml.merchantpin(@password) if @password
478
+ xml.orderid pp_txn_id
479
+ xml.historyid auth
480
+ xml.amount amount if amount
481
+ xml.ipaddress
482
+ }
483
+ }
484
+
485
+ return xml_block
486
+ end
487
+
488
+
489
+
490
+ end # class PrismPay
491
+
492
+
493
+ class BankAccount
494
+ # mimic ActiveMerchant check object for compatibility, tesing, and
495
+ # stand alone purposes
496
+
497
+ attr_accessor :first_name, :last_name, :account_number, :routing_number,
498
+ :account_holder_type, :account_type, :cknumber
499
+
500
+ def number=(n)
501
+ @cknumber = n
502
+ end
503
+
504
+ def number
505
+ @cknumber
506
+ end
507
+
508
+ def [](method)
509
+ eval ("self.#{method}")
510
+ end
511
+
512
+ def []=(method, rval)
513
+ eval ("self.#{method} = rval")
514
+ end
515
+
516
+ def name
517
+ [@first_name, @last_name].join(' ')
518
+ end
519
+
520
+ def name=(n)
521
+ names = n.split(' ')
522
+ @first_name = names[0]
523
+ @last_name = names[1]
524
+ end
525
+
526
+ def initialize(checkinfo = {})
527
+ if checkinfo.respond_to?("has_key?")
528
+ @account_number = checkinfo[:account_number] if checkinfo.has_key?(:account_number)
529
+ @name = checkinfo[:name] if checkinfo.has_key?(:name)
530
+ @routing_number = checkinfo[:routing_number] if checkinfo.has_key?(:routing_number)
531
+ @cknumber = checkinfo[:number] if checkinfo.has_key?(:number)
532
+ @account_type = checkinfo[:account_type] if checkinfo.has_key?(:account_type)
533
+ @account_holder_type = checkinfo[:account_holder_type] if checkinfo.has_key?(:account_holder_type)
534
+ end
535
+ end
536
+
537
+ end # BankAccount
538
+
539
+ class CreditCard
540
+ # credit card information... mimic ActiveMerchant
541
+ attr_accessor :number, :month, :year, :first_name,
542
+ :verification_value, :type, :last_name
543
+
544
+ def [](method)
545
+ eval ("self.#{method}")
546
+ end
547
+
548
+ def name
549
+ join(@first_name, @last_name)
550
+ end
551
+
552
+ def name=(n)
553
+ names = n.split(' ')
554
+ @first_name = names[0]
555
+ @last_name = names[1]
556
+ end
557
+
558
+ def []=(method, rval)
559
+ eval ("self.#{method} = rval")
560
+ end
561
+
562
+ def initialize(ccinfo = {})
563
+ if ccinfo.respond_to?("has_key?")
564
+ @number = ccinfo[:number] if ccinfo.has_key?(:number)
565
+ @month = ccinfo[:month] if ccinfo.has_key?(:month)
566
+ @year = ccinfo[:year] if ccinfo.has_key?(:year)
567
+ @name = ccinfo[:name] if ccinfo.has_key?(:name)
568
+ @verification_value = ccinfo[:verification_value] if ccinfo.has_key?(:verification_value)
569
+ @type = ccinfo[:type] if ccinfo.has_key?(:type)
570
+ end
571
+ end
572
+ end # CreditCard
573
+
574
+ end #module PrismPay
575
+
576
+
577
+ # #####################
578
+ # # Demo driver
579
+ # #####################
580
+
581
+ # cc = CreditCard.new({})
582
+ # cc.number = 5454545454545454
583
+ # cc.month = 7
584
+ # cc.year = 14
585
+ # cc.name = "JohnDoe Soap"
586
+ # cc.verification_value = 123
587
+ # cc.type = "Visa"
588
+
589
+ # addr2 = {
590
+ # :name => "Fathead Don",
591
+ # :address1 => "1501 S Delany Ave",
592
+ # :city => "Orlando",
593
+ # :state => "FL",
594
+ # :zip => "32806",
595
+ # :country => "US"
596
+ # }
597
+
598
+ # options = {
599
+ # :login => "TEST0",
600
+ # :order_id => Time.now.strftime("%y%m%d%H%M%S"),
601
+ # :address => addr2
602
+ # }
603
+
604
+ # gateway = PrismPay.new(options)
605
+
606
+ # purchase_amount = "23.32"
607
+
608
+ # response = gateway.cc_purchase(purchase_amount, cc, options)
609
+
610
+ # ########################################
611
+ # # NOTE
612
+ # ########################################
613
+ # # as of now the cc_purchase and cc_auth method will return the soap
614
+ # # response object from the client that objects meaningful values are
615
+ # # accessible from response.body[:multi_ref].keys()
616
+
617
+ # response = gateway.credit_sale(purchase_amount, credit_card)
618
+
619
+ # puts "The unparsed authcode is #{response.body[:multi_ref][:authcode]}"
620
+