prismpay 0.0.8 → 0.0.9

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.
@@ -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
+