See the remote and mocked unit test files for example usage. Pay special attention to the contents of the options hash.
Initial setup instructions can be found in cybersource.com/support_center/implementation/downloads/soap_api/SOAP_toolkits.pdf
Debugging If you experience an issue with this gateway be sure to examine the transaction information from a general transaction search inside the CyberSource Business Center for the full error messages including field names.
Important Notes
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.
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.
If you want to calculate VAT for overseas customers you must supply a registration number in the options hash as vat_reg_number.
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.
All transactions use dollar values.
These are the options that can be used when creating a new CyberSource Gateway object.
:login => your username
:password => the transaction key you generated in the Business Center
:test => true sets the gateway to test mode
:vat_reg_number => your VAT registration number
:nexus => “WI CA QC” sets the states/provinces where you have a physical presense for tax purposes
:ignore_avs => true don’t want to use AVS so continue processing even if AVS would have failed
:ignore_cvv => true don’t want to use CVV so continue processing even if CVV would have failed
# File lib/active_merchant/billing/gateways/cyber_source.rb, line 93 93: def initialize(options = {}) 94: requires!(options, :login, :password) 95: @options = options 96: super 97: end
# File lib/active_merchant/billing/gateways/cyber_source.rb, line 113 113: def auth_reversal(money, identification, options = {}) 114: commit(build_auth_reversal_request(money, identification, options), options) 115: end
CyberSource requires that you provide line item information for tax calculations 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
The line_item hash goes in the options hash and should look like
:line_items => [ { :declared_value => '1', :quantity => '2', :code => 'default', :description => 'Giant Walrus', :sku => 'WA323232323232323' }, { :declared_value => '6', :quantity => '1', :code => 'default', :description => 'Marble Snowcone', :sku => 'FAKE1232132113123' } ]
This functionality is only supported by this particular gateway may be changed at any time
# File lib/active_merchant/billing/gateways/cyber_source.rb, line 168 168: def calculate_tax(creditcard, options) 169: requires!(options, :line_items) 170: setup_address_hash(options) 171: commit(build_tax_calculation_request(creditcard, options), options) 172: end
Capture an authorization that has previously been requested
# File lib/active_merchant/billing/gateways/cyber_source.rb, line 118 118: def capture(money, authorization, options = {}) 119: setup_address_hash(options) 120: commit(build_capture_request(money, authorization, options), options) 121: end
# File lib/active_merchant/billing/gateways/cyber_source.rb, line 139 139: def credit(money, identification, options = {}) 140: deprecated CREDIT_DEPRECATION_MESSAGE 141: refund(money, identification, options) 142: end
Purchase is an auth followed by a capture You must supply an order_id in the options hash
# File lib/active_merchant/billing/gateways/cyber_source.rb, line 125 125: def purchase(money, creditcard, options = {}) 126: requires!(options, :order_id, :email) 127: setup_address_hash(options) 128: commit(build_purchase_request(money, creditcard, options), options) 129: end
# File lib/active_merchant/billing/gateways/cyber_source.rb, line 135 135: def refund(money, identification, options = {}) 136: commit(build_credit_request(money, identification, options), options) 137: end
# File lib/active_merchant/billing/gateways/cyber_source.rb, line 286 286: def add_address(xml, creditcard, address, options, shipTo = false) 287: xml.tag! shipTo ? 'shipTo' : 'billTo' do 288: xml.tag! 'firstName', creditcard.first_name 289: xml.tag! 'lastName', creditcard.last_name 290: xml.tag! 'street1', address[:address1] 291: xml.tag! 'street2', address[:address2] 292: xml.tag! 'city', address[:city] 293: xml.tag! 'state', address[:state] 294: xml.tag! 'postalCode', address[:zip] 295: xml.tag! 'country', address[:country] 296: xml.tag! 'email', options[:email] 297: end 298: end
# File lib/active_merchant/billing/gateways/cyber_source.rb, line 340 340: def add_auth_reversal_service(xml, request_id, request_token) 341: xml.tag! 'ccAuthReversalService', {'run' => 'true'} do 342: xml.tag! 'authRequestID', request_id 343: xml.tag! 'authRequestToken', request_token 344: end 345: end
# File lib/active_merchant/billing/gateways/cyber_source.rb, line 317 317: def add_auth_service(xml) 318: xml.tag! 'ccAuthService', {'run' => 'true'} 319: end
# File lib/active_merchant/billing/gateways/cyber_source.rb, line 252 252: def add_business_rules_data(xml) 253: xml.tag! 'businessRules' do 254: xml.tag!('ignoreAVSResult', 'true') if @options[:ignore_avs] 255: xml.tag!('ignoreCVResult', 'true') if @options[:ignore_cvv] 256: end 257: end
# File lib/active_merchant/billing/gateways/cyber_source.rb, line 321 321: def add_capture_service(xml, request_id, request_token) 322: xml.tag! 'ccCaptureService', {'run' => 'true'} do 323: xml.tag! 'authRequestID', request_id 324: xml.tag! 'authRequestToken', request_token 325: end 326: end
# File lib/active_merchant/billing/gateways/cyber_source.rb, line 347 347: def add_credit_service(xml, request_id, request_token) 348: xml.tag! 'ccCreditService', {'run' => 'true'} do 349: xml.tag! 'captureRequestID', request_id 350: xml.tag! 'captureRequestToken', request_token 351: end 352: end
# File lib/active_merchant/billing/gateways/cyber_source.rb, line 300 300: def add_creditcard(xml, creditcard) 301: xml.tag! 'card' do 302: xml.tag! 'accountNumber', creditcard.number 303: xml.tag! 'expirationMonth', format(creditcard.month, :two_digits) 304: xml.tag! 'expirationYear', format(creditcard.year, :four_digits) 305: xml.tag!('cvNumber', creditcard.verification_value) unless (@options[:ignore_cvv] || creditcard.verification_value.blank? ) 306: xml.tag! 'cardType', @@credit_card_codes[card_brand(creditcard).to_sym] 307: end 308: end
# File lib/active_merchant/billing/gateways/cyber_source.rb, line 259 259: def add_line_item_data(xml, options) 260: options[:line_items].each_with_index do |value, index| 261: xml.tag! 'item', {'id' => index} do 262: xml.tag! 'unitPrice', amount(value[:declared_value]) 263: xml.tag! 'quantity', value[:quantity] 264: xml.tag! 'productCode', value[:code] || 'shipping_only' 265: xml.tag! 'productName', value[:description] 266: xml.tag! 'productSKU', value[:sku] 267: end 268: end 269: end
# File lib/active_merchant/billing/gateways/cyber_source.rb, line 271 271: def add_merchant_data(xml, options) 272: xml.tag! 'merchantID', @options[:login] 273: xml.tag! 'merchantReferenceCode', options[:order_id] 274: xml.tag! 'clientLibrary' ,'Ruby Active Merchant' 275: xml.tag! 'clientLibraryVersion', '1.0' 276: xml.tag! 'clientEnvironment' , 'Linux' 277: end
# File lib/active_merchant/billing/gateways/cyber_source.rb, line 279 279: def add_purchase_data(xml, money = 0, include_grand_total = false, options={}) 280: xml.tag! 'purchaseTotals' do 281: xml.tag! 'currency', options[:currency] || currency(money) 282: xml.tag!('grandTotalAmount', amount(money)) if include_grand_total 283: end 284: end
# File lib/active_merchant/billing/gateways/cyber_source.rb, line 328 328: def add_purchase_service(xml, options) 329: xml.tag! 'ccAuthService', {'run' => 'true'} 330: xml.tag! 'ccCaptureService', {'run' => 'true'} 331: end
# File lib/active_merchant/billing/gateways/cyber_source.rb, line 310 310: def add_tax_service(xml) 311: xml.tag! 'taxService', {'run' => 'true'} do 312: xml.tag!('nexus', @options[:nexus]) unless @options[:nexus].blank? 313: xml.tag!('sellerRegistration', @options[:vat_reg_number]) unless @options[:vat_reg_number].blank? 314: end 315: end
# File lib/active_merchant/billing/gateways/cyber_source.rb, line 333 333: def add_void_service(xml, request_id, request_token) 334: xml.tag! 'voidService', {'run' => 'true'} do 335: xml.tag! 'voidRequestID', request_id 336: xml.tag! 'voidRequestToken', request_token 337: end 338: end
# File lib/active_merchant/billing/gateways/cyber_source.rb, line 181 181: def build_auth_request(money, creditcard, options) 182: xml = Builder::XmlMarkup.new :indent => 2 183: add_address(xml, creditcard, options[:billing_address], options) 184: add_purchase_data(xml, money, true, options) 185: add_creditcard(xml, creditcard) 186: add_auth_service(xml) 187: add_business_rules_data(xml) 188: xml.target! 189: end
# File lib/active_merchant/billing/gateways/cyber_source.rb, line 232 232: def build_auth_reversal_request(money, identification, options) 233: order_id, request_id, request_token = identification.split(";") 234: options[:order_id] = order_id 235: xml = Builder::XmlMarkup.new :indent => 2 236: add_purchase_data(xml, money, true, options) 237: add_auth_reversal_service(xml, request_id, request_token) 238: xml.target! 239: end
# File lib/active_merchant/billing/gateways/cyber_source.rb, line 202 202: def build_capture_request(money, authorization, options) 203: order_id, request_id, request_token = authorization.split(";") 204: options[:order_id] = order_id 205: 206: xml = Builder::XmlMarkup.new :indent => 2 207: add_purchase_data(xml, money, true, options) 208: add_capture_service(xml, request_id, request_token) 209: add_business_rules_data(xml) 210: xml.target! 211: end
# File lib/active_merchant/billing/gateways/cyber_source.rb, line 241 241: def build_credit_request(money, identification, options) 242: order_id, request_id, request_token = identification.split(";") 243: options[:order_id] = order_id 244: 245: xml = Builder::XmlMarkup.new :indent => 2 246: add_purchase_data(xml, money, true, options) 247: add_credit_service(xml, request_id, request_token) 248: 249: xml.target! 250: end
# File lib/active_merchant/billing/gateways/cyber_source.rb, line 213 213: def build_purchase_request(money, creditcard, options) 214: xml = Builder::XmlMarkup.new :indent => 2 215: add_address(xml, creditcard, options[:billing_address], options) 216: add_purchase_data(xml, money, true, options) 217: add_creditcard(xml, creditcard) 218: add_purchase_service(xml, options) 219: add_business_rules_data(xml) 220: xml.target! 221: end
Where we actually build the full SOAP request using builder
# File lib/active_merchant/billing/gateways/cyber_source.rb, line 356 356: def build_request(body, options) 357: xml = Builder::XmlMarkup.new :indent => 2 358: xml.instruct! 359: xml.tag! 's:Envelope', {'xmlns:s' => 'http://schemas.xmlsoap.org/soap/envelope/'} do 360: xml.tag! 's:Header' do 361: 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 362: xml.tag! 'wsse:UsernameToken' do 363: xml.tag! 'wsse:Username', @options[:login] 364: xml.tag! 'wsse:Password', @options[:password], 'Type' => 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText' 365: end 366: end 367: end 368: xml.tag! 's:Body', {'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema'} do 369: xml.tag! 'requestMessage', {'xmlns' => 'urn:schemas-cybersource-com:transaction-data-1.32'} do 370: add_merchant_data(xml, options) 371: xml << body 372: end 373: end 374: end 375: xml.target! 376: end
# File lib/active_merchant/billing/gateways/cyber_source.rb, line 191 191: def build_tax_calculation_request(creditcard, options) 192: xml = Builder::XmlMarkup.new :indent => 2 193: add_address(xml, creditcard, options[:billing_address], options, false) 194: add_address(xml, creditcard, options[:shipping_address], options, true) 195: add_line_item_data(xml, options) 196: add_purchase_data(xml, 0, false, options) 197: add_tax_service(xml) 198: add_business_rules_data(xml) 199: xml.target! 200: end
# File lib/active_merchant/billing/gateways/cyber_source.rb, line 223 223: def build_void_request(identification, options) 224: order_id, request_id, request_token = identification.split(";") 225: options[:order_id] = order_id 226: 227: xml = Builder::XmlMarkup.new :indent => 2 228: add_void_service(xml, request_id, request_token) 229: xml.target! 230: end
Contact CyberSource, make the SOAP request, and parse the reply into a Response object
# File lib/active_merchant/billing/gateways/cyber_source.rb, line 379 379: def commit(request, options) 380: response = parse(ssl_post(test? ? TEST_URL : LIVE_URL, build_request(request, options))) 381: 382: success = response[:decision] == "ACCEPT" 383: message = @@response_codes[('r' + response[:reasonCode]).to_sym] rescue response[:message] 384: authorization = success ? [ options[:order_id], response[:requestID], response[:requestToken] ].compact.join(";") : nil 385: 386: Response.new(success, message, response, 387: :test => test?, 388: :authorization => authorization, 389: :avs_result => { :code => response[:avsCode] }, 390: :cvv_result => response[:cvCode] 391: ) 392: end
Parse the SOAP response Technique inspired by the Paypal Gateway
# File lib/active_merchant/billing/gateways/cyber_source.rb, line 396 396: def parse(xml) 397: reply = {} 398: xml = REXML::Document.new(xml) 399: if root = REXML::XPath.first(xml, "//c:replyMessage") 400: root.elements.to_a.each do |node| 401: case node.name 402: when 'c:reasonCode' 403: reply[:message] = reply(node.text) 404: else 405: parse_element(reply, node) 406: end 407: end 408: elsif root = REXML::XPath.first(xml, "//soap:Fault") 409: parse_element(reply, root) 410: reply[:message] = "#{reply[:faultcode]}: #{reply[:faultstring]}" 411: end 412: return reply 413: end
# File lib/active_merchant/billing/gateways/cyber_source.rb, line 415 415: def parse_element(reply, node) 416: if node.has_elements? 417: node.elements.each{|e| parse_element(reply, e) } 418: else 419: if node.parent.name =~ /item/ 420: parent = node.parent.name + (node.parent.attributes["id"] ? "_" + node.parent.attributes["id"] : '') 421: reply[(parent + '_' + node.name).to_sym] = node.text 422: else 423: reply[node.name.to_sym] = node.text 424: end 425: end 426: return reply 427: end
Create all address hash key value pairs so that we still function if we were only provided with one or two of them
# File lib/active_merchant/billing/gateways/cyber_source.rb, line 176 176: def setup_address_hash(options) 177: options[:billing_address] = options[:billing_address] || options[:address] || {} 178: options[:shipping_address] = options[:shipping_address] || {} 179: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.