active_merchant_cyber_source_subscriptions 0.1.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.
@@ -0,0 +1,2 @@
1
+ Gemfile.lock
2
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
@@ -0,0 +1,3 @@
1
+ require 'bundler'
2
+
3
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,7 @@
1
+ require 'active_merchant'
2
+
3
+ ActiveMerchant::Billing.class_eval do
4
+ remove_const(:CyberSourceGateway)
5
+ end
6
+
7
+ require 'active_merchant_cyber_source_subscriptions/billing/gateways/cyber_source'
@@ -0,0 +1,526 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ # See the remote and mocked unit test files for example usage. Pay special attention to the contents of the options hash.
4
+ #
5
+ # Initial setup instructions can be found in http://cybersource.com/support_center/implementation/downloads/soap_api/SOAP_toolkits.pdf
6
+ #
7
+ # Debugging
8
+ # If you experience an issue with this gateway be sure to examine the transaction information from a general transaction search inside the CyberSource Business
9
+ # Center for the full error messages including field names.
10
+ #
11
+ # Important Notes
12
+ # * AVS and CVV only work against the production server. You will always get back X for AVS and no response for CVV against the test server.
13
+ # * Nexus is the list of states or provinces where you have a physical presence. Nexus is used to calculate tax. Leave blank to tax everyone.
14
+ # * If you want to calculate VAT for overseas customers you must supply a registration number in the options hash as vat_reg_number.
15
+ # * productCode is a value in the line_items hash that is used to tell CyberSource what kind of item you are selling. It is used when calculating tax/VAT.
16
+ # * All transactions use dollar values.
17
+ class CyberSourceGateway < Gateway
18
+ TEST_URL = 'https://ics2wstest.ic3.com/commerce/1.x/transactionProcessor'
19
+ LIVE_URL = 'https://ics2ws.ic3.com/commerce/1.x/transactionProcessor'
20
+
21
+ # visa, master, american_express, discover
22
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover]
23
+ self.supported_countries = ['US']
24
+ self.default_currency = 'USD'
25
+ self.homepage_url = 'http://www.cybersource.com'
26
+ self.display_name = 'CyberSource'
27
+
28
+ # map credit card to the CyberSource expected representation
29
+ @@credit_card_codes = {
30
+ :visa => '001',
31
+ :master => '002',
32
+ :american_express => '003',
33
+ :discover => '004'
34
+ }
35
+
36
+ # map response codes to something humans can read
37
+ @@response_codes = {
38
+ :r100 => "Successful transaction",
39
+ :r101 => "Request is missing one or more required fields" ,
40
+ :r102 => "One or more fields contains invalid data",
41
+ :r150 => "General failure",
42
+ :r151 => "The request was received but a server time-out occurred",
43
+ :r152 => "The request was received, but a service timed out",
44
+ :r200 => "The authorization request was approved by the issuing bank but declined by CyberSource because it did not pass the AVS check",
45
+ :r201 => "The issuing bank has questions about the request",
46
+ :r202 => "Expired card",
47
+ :r203 => "General decline of the card",
48
+ :r204 => "Insufficient funds in the account",
49
+ :r205 => "Stolen or lost card",
50
+ :r207 => "Issuing bank unavailable",
51
+ :r208 => "Inactive card or card not authorized for card-not-present transactions",
52
+ :r209 => "American Express Card Identifiction Digits (CID) did not match",
53
+ :r210 => "The card has reached the credit limit",
54
+ :r211 => "Invalid card verification number",
55
+ :r221 => "The customer matched an entry on the processor's negative file",
56
+ :r230 => "The authorization request was approved by the issuing bank but declined by CyberSource because it did not pass the card verification check",
57
+ :r231 => "Invalid account number",
58
+ :r232 => "The card type is not accepted by the payment processor",
59
+ :r233 => "General decline by the processor",
60
+ :r234 => "A problem exists with your CyberSource merchant configuration",
61
+ :r235 => "The requested amount exceeds the originally authorized amount",
62
+ :r236 => "Processor failure",
63
+ :r237 => "The authorization has already been reversed",
64
+ :r238 => "The authorization has already been captured",
65
+ :r239 => "The requested transaction amount must match the previous transaction amount",
66
+ :r240 => "The card type sent is invalid or does not correlate with the credit card number",
67
+ :r241 => "The request ID is invalid",
68
+ :r242 => "You requested a capture, but there is no corresponding, unused authorization record.",
69
+ :r243 => "The transaction has already been settled or reversed",
70
+ :r244 => "The bank account number failed the validation check",
71
+ :r246 => "The capture or credit is not voidable because the capture or credit information has already been submitted to your processor",
72
+ :r247 => "You requested a credit for a capture that was previously voided",
73
+ :r250 => "The request was received, but a time-out occurred with the payment processor",
74
+ :r254 => "Your CyberSource account is prohibited from processing stand-alone refunds",
75
+ :r255 => "Your CyberSource account is not configured to process the service in the country you specified"
76
+ }
77
+
78
+ # These are the options that can be used when creating a new CyberSource Gateway object.
79
+ #
80
+ # :login => your username
81
+ #
82
+ # :password => the transaction key you generated in the Business Center
83
+ #
84
+ # :test => true sets the gateway to test mode
85
+ #
86
+ # :vat_reg_number => your VAT registration number
87
+ #
88
+ # :nexus => "WI CA QC" sets the states/provinces where you have a physical presense for tax purposes
89
+ #
90
+ # :ignore_avs => true don't want to use AVS so continue processing even if AVS would have failed
91
+ #
92
+ # :ignore_cvv => true don't want to use CVV so continue processing even if CVV would have failed
93
+ def initialize(options = {})
94
+ requires!(options, :login, :password)
95
+ @options = options
96
+ super
97
+ end
98
+
99
+ # Should run against the test servers or not?
100
+ def test?
101
+ @options[:test] || Base.gateway_mode == :test
102
+ end
103
+
104
+ # Request an authorization for an amount from CyberSource
105
+ #
106
+ # You must supply an :order_id in the options hash
107
+ def authorize(money, creditcard, options = {})
108
+ requires!(options, :order_id, :email)
109
+ setup_address_hash(options)
110
+ commit(build_auth_request(money, creditcard, options), options )
111
+ end
112
+
113
+ # Capture an authorization that has previously been requested
114
+ def capture(money, authorization, options = {})
115
+ setup_address_hash(options)
116
+ commit(build_capture_request(money, authorization, options), options)
117
+ end
118
+
119
+ # Purchase is an auth followed by a capture
120
+ # You must supply an order_id in the options hash
121
+ def purchase(money, creditcard, options = {})
122
+ requires!(options, :order_id, :email)
123
+ setup_address_hash(options)
124
+ commit(build_purchase_request(money, creditcard, options), options)
125
+ end
126
+
127
+ def void(identification, options = {})
128
+ commit(build_void_request(identification, options), options)
129
+ end
130
+
131
+ def credit(money, identification, options = {})
132
+ commit(build_credit_request(money, identification, options), options)
133
+ end
134
+
135
+ def create_subscription(creditcard, options = {})
136
+ requires!(options, :subscription, :billing_address, :order_id, :email)
137
+ requires!(options[:subscription], [:frequency, "on-demand", "weekly", "bi-weekly", "semi-monthly", "quarterly", "quad-weekly", "semi-annually", "annually"],:start_date, :occurrences, :auto_renew)
138
+ requires!(options[:billing_address], :first_name, :last_name)
139
+ setup_address_hash(options)
140
+ commit(build_create_subscription_request(creditcard, options), options)
141
+ end
142
+
143
+ def update_subscription(identification, options = {})
144
+ requires!(options, :order_id)
145
+ setup_address_hash(options)
146
+ commit(build_update_subscription_request(identification, options), options)
147
+ end
148
+
149
+ def retrieve_subscription(identification, options = {})
150
+ commit(build_retrieve_subscription_request(identification, options), options)
151
+ end
152
+
153
+ def subscription_authorization(money, identification, options = {})
154
+ commit(build_subscription_authorization_request(money, identification, options), options)
155
+ end
156
+
157
+ def subscription_purchase(money, identification, options = {})
158
+ commit(build_subscription_purchase_request(money, identification, options), options)
159
+ end
160
+
161
+ # CyberSource requires that you provide line item information for tax calculations
162
+ # If you do not have prices for each item or want to simplify the situation then pass in one fake line item that costs the subtotal of the order
163
+ #
164
+ # The line_item hash goes in the options hash and should look like
165
+ #
166
+ # :line_items => [
167
+ # {
168
+ # :declared_value => '1',
169
+ # :quantity => '2',
170
+ # :code => 'default',
171
+ # :description => 'Giant Walrus',
172
+ # :sku => 'WA323232323232323'
173
+ # },
174
+ # {
175
+ # :declared_value => '6',
176
+ # :quantity => '1',
177
+ # :code => 'default',
178
+ # :description => 'Marble Snowcone',
179
+ # :sku => 'FAKE1232132113123'
180
+ # }
181
+ # ]
182
+ #
183
+ # This functionality is only supported by this particular gateway may
184
+ # be changed at any time
185
+ def calculate_tax(creditcard, options)
186
+ requires!(options, :line_items)
187
+ setup_address_hash(options)
188
+ commit(build_tax_calculation_request(creditcard, options), options)
189
+ end
190
+
191
+ private
192
+ # Create all address hash key value pairs so that we still function if we were only provided with one or two of them
193
+ def setup_address_hash(options)
194
+ options[:billing_address] = options[:billing_address] || options[:address] || {}
195
+ options[:shipping_address] = options[:shipping_address] || {}
196
+ end
197
+
198
+ def build_auth_request(money, creditcard, options)
199
+ xml = Builder::XmlMarkup.new :indent => 2
200
+ add_address(xml, options[:billing_address], options)
201
+ add_address(xml, options[:shipping_address], options, true) if options[:shipping_address]
202
+ add_purchase_data(xml, money, true, options)
203
+ add_creditcard(xml, creditcard)
204
+ add_auth_service(xml)
205
+ add_business_rules_data(xml)
206
+ xml.target!
207
+ end
208
+
209
+ def build_tax_calculation_request(creditcard, options)
210
+ xml = Builder::XmlMarkup.new :indent => 2
211
+ add_address(xml, options[:billing_address], options, false)
212
+ add_address(xml, options[:shipping_address], options, true)
213
+ add_line_item_data(xml, options)
214
+ add_purchase_data(xml, 0, false, options)
215
+ add_tax_service(xml)
216
+ add_business_rules_data(xml)
217
+ xml.target!
218
+ end
219
+
220
+ def build_capture_request(money, authorization, options)
221
+ order_id, request_id, request_token = authorization.split(";")
222
+ options[:order_id] = order_id
223
+
224
+ xml = Builder::XmlMarkup.new :indent => 2
225
+ add_purchase_data(xml, money, true, options)
226
+ add_capture_service(xml, request_id, request_token)
227
+ add_business_rules_data(xml)
228
+ xml.target!
229
+ end
230
+
231
+ def build_purchase_request(money, creditcard, options)
232
+ xml = Builder::XmlMarkup.new :indent => 2
233
+ add_address(xml, options[:billing_address], options)
234
+ add_address(xml, options[:shipping_address], options, true) if options[:shipping_address]
235
+ add_purchase_data(xml, money, true, options)
236
+ add_creditcard(xml, creditcard)
237
+ add_purchase_service(xml, options)
238
+ add_business_rules_data(xml)
239
+ xml.target!
240
+ end
241
+
242
+ def build_void_request(identification, options)
243
+ order_id, request_id, request_token = identification.split(";")
244
+ options[:order_id] = order_id
245
+
246
+ xml = Builder::XmlMarkup.new :indent => 2
247
+ add_void_service(xml, request_id, request_token)
248
+ xml.target!
249
+ end
250
+
251
+ def build_credit_request(money, identification, options)
252
+ order_id, request_id, request_token = identification.split(";")
253
+ options[:order_id] = order_id
254
+
255
+ xml = Builder::XmlMarkup.new :indent => 2
256
+ add_purchase_data(xml, money, true, options)
257
+ add_credit_service(xml, request_id, request_token)
258
+
259
+ xml.target!
260
+ end
261
+
262
+ def build_create_subscription_request(creditcard, options)
263
+ xml = Builder::XmlMarkup.new :indent => 2
264
+ add_address(xml, options[:billing_address], options)
265
+ add_address(xml, options[:shipping_address], options, true)
266
+ add_purchase_data(xml, options[:subscription][:amount], options[:setup_fee], options)
267
+ add_creditcard(xml, creditcard)
268
+ add_subscription(xml, options)
269
+ add_subscription_create_service(xml, options)
270
+ add_business_rules_data(xml)
271
+ xml.target!
272
+ end
273
+
274
+ def build_update_subscription_request(identification, options)
275
+ reference_code, subscription_id, request_token = identification.split(";")
276
+ options[:subscription] ||= {}
277
+ options[:subscription][:subscription_id] = subscription_id
278
+
279
+ xml = Builder::XmlMarkup.new :indent => 2
280
+ add_address(xml, options[:billing_address], options) unless options[:billing_address].empty?
281
+ add_address(xml, options[:shipping_address], options, true) unless options[:shipping_address].empty?
282
+ add_purchase_data(xml, options[:subscription][:amount], options[:setup_fee], options)
283
+ add_creditcard(xml, options[:credit_card]) if options[:credit_card]
284
+ add_subscription(xml, options)
285
+ add_subscription_update_service(xml, options)
286
+ add_business_rules_data(xml)
287
+ xml.target!
288
+ end
289
+
290
+ def build_retrieve_subscription_request(identification, options)
291
+ reference_code, subscription_id, request_token = identification.split(";")
292
+ options[:subscription] ||= {}
293
+ options[:subscription][:subscription_id] = subscription_id
294
+ xml = Builder::XmlMarkup.new :indent => 2
295
+ add_subscription(xml, options)
296
+ add_subscription_retrieve_service(xml, options)
297
+ xml.target!
298
+ end
299
+
300
+ def build_subscription_authorization_request(money, identification, options)
301
+ reference_code, subscription_id, request_token = identification.split(";")
302
+ options[:subscription] ||= {}
303
+ options[:subscription][:subscription_id] = subscription_id
304
+
305
+ xml = Builder::XmlMarkup.new :indent => 2
306
+ add_purchase_data(xml, money, true, options)
307
+ add_subscription(xml, options)
308
+ add_auth_service(xml)
309
+ add_business_rules_data(xml)
310
+ xml.target!
311
+ end
312
+
313
+ def build_subscription_purchase_request(money, identification, options)
314
+ reference_code, subscription_id, request_token = identification.split(";")
315
+ options[:subscription] ||= {}
316
+ options[:subscription][:subscription_id] = subscription_id
317
+
318
+ xml = Builder::XmlMarkup.new :indent => 2
319
+ add_purchase_data(xml, money, true, options)
320
+ add_subscription(xml, options)
321
+ add_purchase_service(xml, options)
322
+ add_business_rules_data(xml)
323
+ xml.target!
324
+ end
325
+
326
+ def add_business_rules_data(xml)
327
+ xml.tag! 'businessRules' do
328
+ xml.tag!('ignoreAVSResult', 'true') if @options[:ignore_avs]
329
+ xml.tag!('ignoreCVResult', 'true') if @options[:ignore_cvv]
330
+ end
331
+ end
332
+
333
+ def add_line_item_data(xml, options)
334
+ options[:line_items].each_with_index do |value, index|
335
+ xml.tag! 'item', {'id' => index} do
336
+ xml.tag! 'unitPrice', amount(value[:declared_value])
337
+ xml.tag! 'quantity', value[:quantity]
338
+ xml.tag! 'productCode', value[:code] || 'shipping_only'
339
+ xml.tag! 'productName', value[:description]
340
+ xml.tag! 'productSKU', value[:sku]
341
+ end
342
+ end
343
+ end
344
+
345
+ def add_merchant_data(xml, options)
346
+ xml.tag! 'merchantID', @options[:login]
347
+ xml.tag! 'merchantReferenceCode', options[:order_id] || '1111'
348
+ xml.tag! 'clientLibrary' ,'Ruby Active Merchant'
349
+ xml.tag! 'clientLibraryVersion', '1.0'
350
+ xml.tag! 'clientEnvironment' , 'Linux'
351
+ end
352
+
353
+ def add_purchase_data(xml, money = 0, include_grand_total = false, options={})
354
+ xml.tag! 'purchaseTotals' do
355
+ xml.tag! 'currency', options[:currency] || currency(money)
356
+ money == 0 ? xml.tag!('grandTotalAmount','0.0') : xml.tag!('grandTotalAmount', amount(money)) if include_grand_total
357
+ end
358
+ end
359
+
360
+ def add_address(xml, address, options, shipTo = false)
361
+ xml.tag! shipTo ? 'shipTo' : 'billTo' do
362
+ xml.tag! 'firstName', address[:first_name]
363
+ xml.tag! 'lastName', address[:last_name]
364
+ xml.tag! 'street1', address[:address1]
365
+ xml.tag! 'street2', address[:address2]
366
+ xml.tag! 'city', address[:city]
367
+ xml.tag! 'state', address[:state]
368
+ xml.tag! 'postalCode', address[:zip]
369
+ xml.tag! 'country', address[:country]
370
+ xml.tag! 'email', options[:email]
371
+ end
372
+ end
373
+
374
+ def add_creditcard(xml, creditcard)
375
+ xml.tag! 'card' do
376
+ xml.tag! 'accountNumber', creditcard.number
377
+ xml.tag! 'expirationMonth', format(creditcard.month, :two_digits)
378
+ xml.tag! 'expirationYear', format(creditcard.year, :four_digits)
379
+ xml.tag!('cvNumber', creditcard.verification_value) unless (@options[:ignore_cvv] || creditcard.verification_value.blank? )
380
+ xml.tag! 'cardType', @@credit_card_codes[card_brand(creditcard).to_sym]
381
+ end
382
+ end
383
+
384
+ def add_tax_service(xml)
385
+ xml.tag! 'taxService', {'run' => 'true'} do
386
+ xml.tag!('nexus', @options[:nexus]) unless @options[:nexus].blank?
387
+ xml.tag!('sellerRegistration', @options[:vat_reg_number]) unless @options[:vat_reg_number].blank?
388
+ end
389
+ end
390
+
391
+ def add_auth_service(xml)
392
+ xml.tag! 'ccAuthService', {'run' => 'true'}
393
+ end
394
+
395
+ def add_capture_service(xml, request_id, request_token)
396
+ xml.tag! 'ccCaptureService', {'run' => 'true'} do
397
+ xml.tag! 'authRequestID', request_id
398
+ xml.tag! 'authRequestToken', request_token
399
+ end
400
+ end
401
+
402
+ def add_purchase_service(xml, options)
403
+ xml.tag! 'ccAuthService', {'run' => 'true'}
404
+ xml.tag! 'ccCaptureService', {'run' => 'true'}
405
+ end
406
+
407
+ def add_void_service(xml, request_id, request_token)
408
+ xml.tag! 'voidService', {'run' => 'true'} do
409
+ xml.tag! 'voidRequestID', request_id
410
+ xml.tag! 'voidRequestToken', request_token
411
+ end
412
+ end
413
+
414
+ def add_credit_service(xml, request_id, request_token)
415
+ xml.tag! 'ccCreditService', {'run' => 'true'} do
416
+ xml.tag! 'captureRequestID', request_id
417
+ xml.tag! 'captureRequestToken', request_token
418
+ end
419
+ end
420
+
421
+ def add_subscription_create_service(xml, options)
422
+ add_auth_service(xml) if options[:setup_fee]
423
+ xml.tag! 'paySubscriptionCreateService', {'run' => 'true'}
424
+ end
425
+
426
+ def add_subscription_update_service(xml, options)
427
+ add_auth_service(xml) if options[:setup_fee]
428
+ xml.tag! 'paySubscriptionUpdateService', {'run' => 'true'}
429
+ end
430
+
431
+ def add_subscription_retrieve_service(xml, options)
432
+ xml.tag! 'paySubscriptionRetrieveService', {'run' => 'true'}
433
+ end
434
+
435
+ def add_subscription(xml, options)
436
+ xml.tag! 'recurringSubscriptionInfo' do
437
+ xml.tag! 'subscriptionID', options[:subscription][:subscription_id]
438
+ xml.tag! 'status', options[:subscription][:status] if options[:subscription][:status]
439
+ xml.tag! 'amount', options[:subscription][:amount] if options[:subscription][:amount]
440
+ xml.tag! 'numberOfPayments', options[:subscription][:occurrences] if options[:subscription][:occurrences]
441
+ xml.tag! 'automaticRenew', options[:subscription][:auto_renew] if options[:subscription][:auto_renew]
442
+ xml.tag! 'frequency', options[:subscription][:frequency] if options[:subscription][:frequency]
443
+ xml.tag! 'startDate', options[:subscription][:start_date].strftime("%Y%m%d") if options[:subscription][:start_date]
444
+ xml.tag! 'endDate', options[:subscription][:end_date].strftime("%Y%m%d") if options[:subscription][:end_date]
445
+ xml.tag! 'approvalRequired', options[:subscription][:approval_required] || false
446
+ xml.tag! 'event', options[:subscription][:event] if options[:subscription][:event]
447
+ xml.tag! 'billPayment', options[:subscription][:bill_payment] if options[:subscription][:bill_payment]
448
+ end
449
+ end
450
+
451
+ # Where we actually build the full SOAP request using builder
452
+ def build_request(body, options)
453
+ xml = Builder::XmlMarkup.new :indent => 2
454
+ xml.instruct!
455
+ xml.tag! 's:Envelope', {'xmlns:s' => 'http://schemas.xmlsoap.org/soap/envelope/'} do
456
+ xml.tag! 's:Header' do
457
+ xml.tag! 'wsse:Security', {'s:mustUnderstand' => '1', 'xmlns:wsse' => 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd'} do
458
+ xml.tag! 'wsse:UsernameToken' do
459
+ xml.tag! 'wsse:Username', @options[:login]
460
+ xml.tag! 'wsse:Password', @options[:password], 'Type' => 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText'
461
+ end
462
+ end
463
+ end
464
+ xml.tag! 's:Body', {'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema'} do
465
+ xml.tag! 'requestMessage', {'xmlns' => 'urn:schemas-cybersource-com:transaction-data-1.32'} do
466
+ add_merchant_data(xml, options)
467
+ xml << body
468
+ end
469
+ end
470
+ end
471
+ xml.target!
472
+ end
473
+
474
+ # Contact CyberSource, make the SOAP request, and parse the reply into a Response object
475
+ def commit(request, options)
476
+ response = parse(ssl_post(test? ? TEST_URL : LIVE_URL, build_request(request, options)))
477
+
478
+ success = response[:decision] == "ACCEPT"
479
+ message = @@response_codes[('r' + response[:reasonCode]).to_sym] rescue response[:message]
480
+ authorization = success ? [ options[:order_id], response[:requestID], response[:requestToken] ].compact.join(";") : nil
481
+
482
+ Response.new(success, message, response,
483
+ :test => test?,
484
+ :authorization => authorization,
485
+ :avs_result => { :code => response[:avsCode] },
486
+ :cvv_result => response[:cvCode]
487
+ )
488
+ end
489
+
490
+ # Parse the SOAP response
491
+ # Technique inspired by the Paypal Gateway
492
+ def parse(xml)
493
+ reply = {}
494
+ xml = REXML::Document.new(xml)
495
+ if root = REXML::XPath.first(xml, "//c:replyMessage")
496
+ root.elements.to_a.each do |node|
497
+ case node.name
498
+ when 'c:reasonCode'
499
+ reply[:message] = reply(node.text)
500
+ else
501
+ parse_element(reply, node)
502
+ end
503
+ end
504
+ elsif root = REXML::XPath.first(xml, "//soap:Fault")
505
+ parse_element(reply, root)
506
+ reply[:message] = "#{reply[:faultcode]}: #{reply[:faultstring]}"
507
+ end
508
+ return reply
509
+ end
510
+
511
+ def parse_element(reply, node)
512
+ if node.has_elements?
513
+ node.elements.each{|e| parse_element(reply, e) }
514
+ else
515
+ if node.parent.name =~ /item/
516
+ parent = node.parent.name + (node.parent.attributes["id"] ? "_" + node.parent.attributes["id"] : '')
517
+ reply[(parent + '_' + node.name).to_sym] = node.text
518
+ else
519
+ reply[node.name.to_sym] = node.text
520
+ end
521
+ end
522
+ return reply
523
+ end
524
+ end
525
+ end
526
+ end
@@ -0,0 +1,3 @@
1
+ module ActiveMerchantCyberSourceSubscriptions
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: active_merchant_cyber_source_subscriptions
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Donald Ball
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-04-18 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: activemerchant
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 0
31
+ version: "0"
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: rake
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ~>
41
+ - !ruby/object:Gem::Version
42
+ hash: 63
43
+ segments:
44
+ - 0
45
+ - 9
46
+ - 2
47
+ version: 0.9.2
48
+ type: :development
49
+ version_requirements: *id002
50
+ description: Replaces the cyber source gateway with one capable of subscriptions.
51
+ email:
52
+ - donald.ball@gmail.com
53
+ executables: []
54
+
55
+ extensions: []
56
+
57
+ extra_rdoc_files: []
58
+
59
+ files:
60
+ - .gitignore
61
+ - Gemfile
62
+ - Rakefile
63
+ - lib/active_merchant_cyber_source_subscriptions.rb
64
+ - lib/active_merchant_cyber_source_subscriptions/billing/gateways/cyber_source.rb
65
+ - lib/active_merchant_cyber_source_subscriptions/version.rb
66
+ homepage: http://github.com/Decisiv/active_merchant_cyber_source_subscriptions/
67
+ licenses: []
68
+
69
+ post_install_message:
70
+ rdoc_options:
71
+ - --charset=UTF-8
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ hash: 3
80
+ segments:
81
+ - 0
82
+ version: "0"
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ hash: 3
89
+ segments:
90
+ - 0
91
+ version: "0"
92
+ requirements: []
93
+
94
+ rubyforge_project:
95
+ rubygems_version: 1.8.17
96
+ signing_key:
97
+ specification_version: 3
98
+ summary: Replaces the cyber source gateway with one capable of subscriptions.
99
+ test_files: []
100
+