Class: ActiveMerchant::Billing::CyberSourceGateway
- Inherits:
-
Object
- Object
- ActiveMerchant::Billing::CyberSourceGateway
- Defined in:
- lib/cybersource/ext/active_merchant/active_merchant.rb
Class Method Summary collapse
- .user_agent ⇒ Object
-
.x_request_id ⇒ Object
The payload definitions: ics2wstest.ic3.com/commerce/1.x/transactionProcessor/CyberSourceTransaction_1.109.xsd.
Instance Method Summary collapse
-
#add_auth_service(xml, payment_method, options) ⇒ Object
Add support for commerceIndicator override.
-
#add_business_rules_data(xml, payment_method, options) ⇒ Object
Changes: * Enable business rules for Apple Pay * Set paymentNetworkToken and paymentSolution if needed (a bit of a hack to do it here, but it avoids having to override too much code).
- #add_capture_service(xml, request_id, request_token, options = {}) ⇒ Object
- #add_credit_service(xml, request_id = nil, request_token = nil, options = {}) ⇒ Object
- #add_invoice_header(xml, options) ⇒ Object
- #add_merchant_data(xml, options) ⇒ Object
-
#add_network_tokenization(xml, payment_method, options) ⇒ Object
Changes: * Add support for commerceIndicator override * Don’t set paymentNetworkToken (needs to be set after businessRules) * Fix typo (expected brand for MasterCard is master).
-
#add_payment_solution(xml, payment_method, options) ⇒ Object
Changes: * apps.cybersource.com/library/documentation/dev_guides/Android_Pay_SO_API/html/wwhelp/wwhimpl/js/html/wwhelp.htm#href=ch_soAPI.html * add paymentSolution tag to support Android Pay.
- #add_reconciliation_id(xml, options) ⇒ Object
- #build_capture_request(money, authorization, options) ⇒ Object
-
#build_credit_request(money, creditcard_or_reference, options) ⇒ Object
Add support for CreditCard objects.
- #build_headers(options) ⇒ Object
- #build_refund_request(money, identification, options) ⇒ Object
- #commit(request, options) ⇒ Object
- #format_contact(contact) ⇒ Object
- #format_name(name) ⇒ Object
- #format_string(str, max_length) ⇒ Object
-
#initialize(options = {}) ⇒ CyberSourceGateway
constructor
A new instance of CyberSourceGateway.
- #is_android_pay(payment_method, options) ⇒ Object
- #is_google_pay(payment_method, options) ⇒ Object
- #parse_element(reply, node) ⇒ Object
Constructor Details
#initialize(options = {}) ⇒ CyberSourceGateway
Returns a new instance of CyberSourceGateway.
14 15 16 17 18 19 20 |
# File 'lib/cybersource/ext/active_merchant/active_merchant.rb', line 14 def initialize( = {}) super # Add missing response codes @@response_codes[:r104] = 'The merchant reference code for this authorization request matches the merchant reference code of another authorization request that you sent within the past 15 minutes.' @@response_codes[:r110] = 'Only a partial amount was approved' end |
Class Method Details
.user_agent ⇒ Object
210 211 212 213 214 215 216 217 218 |
# File 'lib/cybersource/ext/active_merchant/active_merchant.rb', line 210 def self.user_agent @@ua ||= JSON.dump({ :bindings_version => KB_PLUGIN_VERSION, :lang => 'ruby', :lang_version => "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})", :platform => RUBY_PLATFORM, :publisher => 'killbill' }) end |
.x_request_id ⇒ Object
The payload definitions: ics2wstest.ic3.com/commerce/1.x/transactionProcessor/CyberSourceTransaction_1.109.xsd
9 10 11 12 |
# File 'lib/cybersource/ext/active_merchant/active_merchant.rb', line 9 def self.x_request_id # See KillbillMDCInsertingServletFilter org::slf4j::MDC::get('req.requestId') rescue nil end |
Instance Method Details
#add_auth_service(xml, payment_method, options) ⇒ Object
Add support for commerceIndicator override
35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/cybersource/ext/active_merchant/active_merchant.rb', line 35 def add_auth_service(xml, payment_method, ) if network_tokenization?(payment_method) add_network_tokenization(xml, payment_method, ) else xml.tag! 'ccAuthService', {'run' => 'true'} do # Let CyberSource figure it out otherwise (internet is the default unless tokens are used) xml.tag!("commerceIndicator", [:commerce_indicator]) unless [:commerce_indicator].blank? add_reconciliation_id(xml, ) end end end |
#add_business_rules_data(xml, payment_method, options) ⇒ Object
Changes:
* Enable business rules for Apple Pay
* Set paymentNetworkToken and paymentSolution if needed (a bit of a hack to do it here, but it avoids having to override too much code)
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/cybersource/ext/active_merchant/active_merchant.rb', line 152 def add_business_rules_data(xml, payment_method, ) = [, @options] xml.tag! 'businessRules' do xml.tag!('ignoreAVSResult', 'true') if extract_option(, :ignore_avs) xml.tag!('ignoreCVResult', 'true') if extract_option(, :ignore_cvv) end if network_tokenization?(payment_method) xml.tag! 'paymentNetworkToken' do xml.tag!('transactionType', "1") end add_payment_solution(xml, payment_method, ) end end |
#add_capture_service(xml, request_id, request_token, options = {}) ⇒ Object
101 102 103 104 105 106 107 |
# File 'lib/cybersource/ext/active_merchant/active_merchant.rb', line 101 def add_capture_service(xml, request_id, request_token, = {}) xml.tag! 'ccCaptureService', {'run' => 'true'} do xml.tag! 'authRequestID', request_id add_reconciliation_id(xml, ) # the order is important xml.tag! 'authRequestToken', request_token end end |
#add_credit_service(xml, request_id = nil, request_token = nil, options = {}) ⇒ Object
120 121 122 123 124 125 126 |
# File 'lib/cybersource/ext/active_merchant/active_merchant.rb', line 120 def add_credit_service(xml, request_id = nil, request_token = nil, = {}) xml.tag! 'ccCreditService', {'run' => 'true'} do xml.tag! 'captureRequestID', request_id if request_id add_reconciliation_id(xml, ) xml.tag! 'captureRequestToken', request_token if request_token end end |
#add_invoice_header(xml, options) ⇒ Object
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 |
# File 'lib/cybersource/ext/active_merchant/active_merchant.rb', line 240 def add_invoice_header(xml, ) merchant_descriptor = [:merchant_descriptor] if merchant_descriptor.present? && merchant_descriptor.is_a?(Hash) && !merchant_descriptor['card_type'].nil? && !merchant_descriptor['transaction_type'].nil? name = merchant_descriptor['name'] contact = merchant_descriptor['contact'] if merchant_descriptor['card_type'].to_s == 'american_express' unless merchant_descriptor['transaction_type'] == :AUTHORIZE # Amex only supports capture and refund xml.tag! 'invoiceHeader' do xml.tag! 'amexDataTAA1', format_string(name, 40) xml.tag! 'amexDataTAA2', format_string(contact, 40) end end else xml.tag! 'invoiceHeader' do xml.tag! 'merchantDescriptor', format_name(name) xml.tag! 'merchantDescriptorContact', format_contact(contact) end end end end |
#add_merchant_data(xml, options) ⇒ Object
231 232 233 234 235 236 237 238 |
# File 'lib/cybersource/ext/active_merchant/active_merchant.rb', line 231 def add_merchant_data(xml, ) xml.tag! 'merchantID', @options[:login] xml.tag! 'merchantReferenceCode', [:order_id] xml.tag! 'clientLibrary' ,'Kill Bill' xml.tag! 'clientLibraryVersion', KB_PLUGIN_VERSION xml.tag! 'clientEnvironment' , RUBY_PLATFORM add_invoice_header(xml, ) # Merchant soft descriptor end |
#add_network_tokenization(xml, payment_method, options) ⇒ Object
Changes:
* Add support for commerceIndicator override
* Don't set paymentNetworkToken (needs to be set after businessRules)
* Fix typo (expected brand for MasterCard is master)
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/cybersource/ext/active_merchant/active_merchant.rb', line 51 def add_network_tokenization(xml, payment_method, ) return unless network_tokenization?(payment_method) case card_brand(payment_method).to_sym when :visa xml.tag! 'ccAuthService', {'run' => 'true'} do xml.tag!("cavv", payment_method.payment_cryptogram) xml.tag!("commerceIndicator", [:commerce_indicator] || (is_android_pay(payment_method, ) || is_google_pay(payment_method, )) ? 'internet' : 'vbv') xml.tag!("xid", payment_method.payment_cryptogram) add_reconciliation_id(xml, ) end when :master xml.tag! 'ucaf' do xml.tag!("authenticationData", payment_method.payment_cryptogram) xml.tag!("collectionIndicator", "2") end xml.tag! 'ccAuthService', {'run' => 'true'} do xml.tag!("commerceIndicator", [:commerce_indicator] || "spa") add_reconciliation_id(xml, ) end when :american_express cryptogram = Base64.decode64(payment_method.payment_cryptogram) xml.tag! 'ccAuthService', {'run' => 'true'} do xml.tag!("cavv", Base64.encode64(cryptogram[0...20])) xml.tag!("commerceIndicator", [:commerce_indicator] || "aesk") if cryptogram.size == 40 xml.tag!("xid", Base64.encode64(cryptogram[20...40])) end add_reconciliation_id(xml, ) end when :discover xml.tag! 'ccAuthService', {'run' => 'true'} do xml.tag!("cavv", payment_method.payment_cryptogram) xml.tag!("commerceIndicator", [:commerce_indicator] || "dipb") add_reconciliation_id(xml, ) end end end |
#add_payment_solution(xml, payment_method, options) ⇒ Object
Changes:
* http://apps.cybersource.com/library/documentation/dev_guides/Android_Pay_SO_API/html/wwhelp/wwhimpl/js/html/wwhelp.htm#href=ch_soAPI.html
* add paymentSolution tag to support Android Pay
131 132 133 134 135 136 137 138 139 |
# File 'lib/cybersource/ext/active_merchant/active_merchant.rb', line 131 def add_payment_solution(xml, payment_method, ) if is_android_pay(payment_method, ) xml.tag!('paymentSolution', '006') elsif is_google_pay(payment_method, ) xml.tag!('paymentSolution', '012') else xml.tag!('paymentSolution', '001') end end |
#add_reconciliation_id(xml, options) ⇒ Object
264 265 266 267 |
# File 'lib/cybersource/ext/active_merchant/active_merchant.rb', line 264 def add_reconciliation_id(xml, ) return unless [:reconciliation_id].present? xml.tag! 'reconciliationID', [:reconciliation_id] end |
#build_capture_request(money, authorization, options) ⇒ Object
90 91 92 93 94 95 96 97 98 99 |
# File 'lib/cybersource/ext/active_merchant/active_merchant.rb', line 90 def build_capture_request(money, , ) order_id, request_id, request_token = .split(";") [:order_id] = order_id xml = Builder::XmlMarkup.new :indent => 2 add_purchase_data(xml, money, true, ) add_capture_service(xml, request_id, request_token, ) add_business_rules_data(xml, , ) xml.target! end |
#build_credit_request(money, creditcard_or_reference, options) ⇒ Object
Add support for CreditCard objects
23 24 25 26 27 28 29 30 31 32 |
# File 'lib/cybersource/ext/active_merchant/active_merchant.rb', line 23 def build_credit_request(money, creditcard_or_reference, ) xml = Builder::XmlMarkup.new :indent => 2 setup_address_hash() add_payment_method_or_subscription(xml, money, creditcard_or_reference, ) add_credit_service(xml) xml.target! end |
#build_headers(options) ⇒ Object
220 221 222 223 224 225 226 227 228 229 |
# File 'lib/cybersource/ext/active_merchant/active_merchant.rb', line 220 def build_headers() [:x_request_id] ||= self.class.x_request_id [:content_type] ||= 'text/xml' headers = {} headers['Content-Type'] = [:content_type] headers['User-Agent'] = self.class.user_agent headers['X-Request-Id'] = [:x_request_id] unless [:x_request_id].blank? headers end |
#build_refund_request(money, identification, options) ⇒ Object
109 110 111 112 113 114 115 116 117 118 |
# File 'lib/cybersource/ext/active_merchant/active_merchant.rb', line 109 def build_refund_request(money, identification, ) order_id, request_id, request_token = identification.split(";") [:order_id] = order_id xml = Builder::XmlMarkup.new :indent => 2 add_purchase_data(xml, money, true, ) add_credit_service(xml, request_id, request_token, ) xml.target! end |
#commit(request, options) ⇒ Object
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
# File 'lib/cybersource/ext/active_merchant/active_merchant.rb', line 169 def commit(request, ) request = build_request(request, ) begin raw_response = ssl_post(test? ? self.test_url : self.live_url, request, build_headers()) rescue ResponseError => e if !e.response.nil? && e.response.code.to_i == 500 && !e.response.body.blank? # See https://github.com/killbill/killbill-cybersource-plugin/issues/4 raw_response = e.response.body else # Don't swallow other 5xx errors like proxy timeouts - these should most likely be UNKNOWN raise e end end response = parse(raw_response) # Remove namespace when unnecessary (ActiveMerchant and our original code expect it that way) response.keys.each do |k| _, actual_key = k.to_s.split('_', 2) if !actual_key.nil? && !response.has_key?(actual_key) response[actual_key] = response[k] response.delete(k) end end response[:reconciliation_id] = [:reconciliation_id] if [:reconciliation_id].present? success = response[:decision] == 'ACCEPT' = success ? [[:order_id], response['subscriptionID'] || response[:requestID], response[:requestToken]].compact.join(';') : nil = nil if response[:reasonCode].blank? && (response[:faultcode] == 'wsse:FailedCheck' || response[:faultcode] == 'wsse:InvalidSecurity' || response[:faultcode] == 'soap:Client' || response[:faultcode] == 'c:ServerError') = {:exception_message => response[:message], :payment_plugin_status => :CANCELED}.to_json end ||= @@response_codes[('r' + response[:reasonCode].to_s).to_sym] || response[:message] Response.new(success, , response, :test => test?, :authorization => , :avs_result => {:code => response['avsCode']}, :cvv_result => response['cvCode']) end |
#format_contact(contact) ⇒ Object
291 292 293 294 295 |
# File 'lib/cybersource/ext/active_merchant/active_merchant.rb', line 291 def format_contact(contact) contact ||= '' contact = contact.gsub(/\D/, '').ljust(10, '0') [contact[0..2],contact[3..5],contact[6..9]].join('-') end |
#format_name(name) ⇒ Object
297 298 299 300 301 302 303 304 |
# File 'lib/cybersource/ext/active_merchant/active_merchant.rb', line 297 def format_name(name) name ||= '' if name.index('*') != nil subnames = name.split('*') name = subnames[0].ljust(12)[0..11] + '*' + subnames[1] end name.ljust(22)[0..21] end |
#format_string(str, max_length) ⇒ Object
286 287 288 289 |
# File 'lib/cybersource/ext/active_merchant/active_merchant.rb', line 286 def format_string(str, max_length) return '' if str.nil? str.first(max_length) end |
#is_android_pay(payment_method, options) ⇒ Object
141 142 143 |
# File 'lib/cybersource/ext/active_merchant/active_merchant.rb', line 141 def is_android_pay(payment_method, ) (payment_method.respond_to?(:source) && payment_method.source == :android_pay) || [:source] == 'androidpay' end |
#is_google_pay(payment_method, options) ⇒ Object
145 146 147 |
# File 'lib/cybersource/ext/active_merchant/active_merchant.rb', line 145 def is_google_pay(payment_method, ) (payment_method.respond_to?(:source) && payment_method.source == :google_pay) || [:source] == 'googlepay' end |
#parse_element(reply, node) ⇒ Object
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 |
# File 'lib/cybersource/ext/active_merchant/active_merchant.rb', line 269 def parse_element(reply, node) if node.has_elements? node.elements.each { |e| parse_element(reply, e) } else # The original ActiveMerchant implementation clobbers top level fields with values from the children # Instead, always namespace the keys for children (cleanup is needed afterwards, see above) is_top_level = node.parent.name == 'replyMessage' || node.parent.name == 'Fault' key = node.name.to_sym unless is_top_level parent = node.parent.name + (node.parent.attributes['id'] ? '_' + node.parent.attributes['id'] : '') key = (parent + '_' + node.name).to_sym end reply[key] = node.text end return reply end |