active_merchant_cyber_source_subscriptions 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+