lib/active_merchant/billing/gateways/cyber_source.rb in activemerchant-1.24.0 vs lib/active_merchant/billing/gateways/cyber_source.rb in activemerchant-1.25.0

- old
+ new

@@ -15,10 +15,12 @@ # * 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. class CyberSourceGateway < Gateway TEST_URL = 'https://ics2wstest.ic3.com/commerce/1.x/transactionProcessor' LIVE_URL = 'https://ics2ws.ic3.com/commerce/1.x/transactionProcessor' + + XSD_VERSION = "1.69" # visa, master, american_express, discover self.supported_cardtypes = [:visa, :master, :american_express, :discover] self.supported_countries = ['US'] self.default_currency = 'USD' @@ -102,14 +104,14 @@ end # Request an authorization for an amount from CyberSource # # You must supply an :order_id in the options hash - def authorize(money, creditcard, options = {}) - requires!(options, :order_id, :email) + def authorize(money, creditcard_or_reference, options = {}) + requires!(options, :order_id) setup_address_hash(options) - commit(build_auth_request(money, creditcard, options), options ) + commit(build_auth_request(money, creditcard_or_reference, options), options ) end def auth_reversal(money, identification, options = {}) commit(build_auth_reversal_request(money, identification, options), options) end @@ -120,14 +122,14 @@ commit(build_capture_request(money, authorization, options), options) end # Purchase is an auth followed by a capture # You must supply an order_id in the options hash - def purchase(money, creditcard, options = {}) - requires!(options, :order_id, :email) + def purchase(money, creditcard_or_reference, options = {}) + requires!(options, :order_id) setup_address_hash(options) - commit(build_purchase_request(money, creditcard, options), options) + commit(build_purchase_request(money, creditcard_or_reference, options), options) end def void(identification, options = {}) commit(build_void_request(identification, options), options) end @@ -139,10 +141,37 @@ def credit(money, identification, options = {}) deprecated CREDIT_DEPRECATION_MESSAGE refund(money, identification, options) end + # Stores a customer subscription/profile with type "on-demand". + # To charge the card while creating a profile, pass options[:setup_fee] => money + def store(creditcard, options = {}) + requires!(options, :order_id) + setup_address_hash(options) + commit(build_create_subscription_request(creditcard, options), options) + end + + # Updates a customer subscription/profile + def update(reference, creditcard, options = {}) + requires!(options, :order_id) + setup_address_hash(options) + commit(build_update_subscription_request(reference, creditcard, options), options) + end + + # Removes a customer subscription/profile + def unstore(reference, options = {}) + requires!(options, :order_id) + commit(build_delete_subscription_request(reference, options), options) + end + + # Retrieves a customer subscription/profile + def retrieve(reference, options = {}) + requires!(options, :order_id) + commit(build_retrieve_subscription_request(reference, options), options) + 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 # @@ -169,22 +198,21 @@ requires!(options, :line_items) setup_address_hash(options) commit(build_tax_calculation_request(creditcard, options), options) end - private + private + # Create all address hash key value pairs so that we still function if we were only provided with one or two of them def setup_address_hash(options) options[:billing_address] = options[:billing_address] || options[:address] || {} options[:shipping_address] = options[:shipping_address] || {} end - def build_auth_request(money, creditcard, options) + def build_auth_request(money, creditcard_or_reference, options) xml = Builder::XmlMarkup.new :indent => 2 - add_address(xml, creditcard, options[:billing_address], options) - add_purchase_data(xml, money, true, options) - add_creditcard(xml, creditcard) + add_creditcard_or_subscription(xml, money, creditcard_or_reference, options) add_auth_service(xml) add_business_rules_data(xml) xml.target! end @@ -208,15 +236,13 @@ add_capture_service(xml, request_id, request_token) add_business_rules_data(xml) xml.target! end - def build_purchase_request(money, creditcard, options) + def build_purchase_request(money, creditcard_or_reference, options) xml = Builder::XmlMarkup.new :indent => 2 - add_address(xml, creditcard, options[:billing_address], options) - add_purchase_data(xml, money, true, options) - add_creditcard(xml, creditcard) + add_creditcard_or_subscription(xml, money, creditcard_or_reference, options) add_purchase_service(xml, options) add_business_rules_data(xml) xml.target! end @@ -247,10 +273,51 @@ add_credit_service(xml, request_id, request_token) xml.target! end + def build_create_subscription_request(creditcard, options) + options[:subscription] = (options[:subscription] || {}).merge(:frequency => "on-demand", :amount => 0, :automatic_renew => false) + + xml = Builder::XmlMarkup.new :indent => 2 + add_address(xml, creditcard, options[:billing_address], options) + add_purchase_data(xml, options[:setup_fee] || 0, true, options) + add_creditcard(xml, creditcard) + add_creditcard_payment_method(xml) + add_subscription(xml, options) + add_purchase_service(xml, options) if options[:setup_fee] + add_subscription_create_service(xml, options) + add_business_rules_data(xml) + xml.target! + end + + def build_update_subscription_request(reference, creditcard, options) + xml = Builder::XmlMarkup.new :indent => 2 + add_address(xml, creditcard, options[:billing_address], options) unless options[:billing_address].blank? + add_purchase_data(xml, options[:setup_fee], true, options) unless options[:setup_fee].blank? + add_creditcard(xml, creditcard) if creditcard + add_creditcard_payment_method(xml) if creditcard + add_subscription(xml, options, reference) + add_subscription_update_service(xml, options) + add_business_rules_data(xml) + xml.target! + end + + def build_delete_subscription_request(reference, options) + xml = Builder::XmlMarkup.new :indent => 2 + add_subscription(xml, options, reference) + add_subscription_delete_service(xml, options) + xml.target! + end + + def build_retrieve_subscription_request(reference, options) + xml = Builder::XmlMarkup.new :indent => 2 + add_subscription(xml, options, reference) + add_subscription_retrieve_service(xml, options) + xml.target! + end + def add_business_rules_data(xml) xml.tag! 'businessRules' do xml.tag!('ignoreAVSResult', 'true') if @options[:ignore_avs] xml.tag!('ignoreCVResult', 'true') if @options[:ignore_cvv] end @@ -270,32 +337,39 @@ def add_merchant_data(xml, options) xml.tag! 'merchantID', @options[:login] xml.tag! 'merchantReferenceCode', options[:order_id] xml.tag! 'clientLibrary' ,'Ruby Active Merchant' - xml.tag! 'clientLibraryVersion', '1.0' - xml.tag! 'clientEnvironment' , 'Linux' + xml.tag! 'clientLibraryVersion', VERSION + xml.tag! 'clientEnvironment' , RUBY_PLATFORM end def add_purchase_data(xml, money = 0, include_grand_total = false, options={}) xml.tag! 'purchaseTotals' do xml.tag! 'currency', options[:currency] || currency(money) xml.tag!('grandTotalAmount', amount(money)) if include_grand_total end end - def add_address(xml, creditcard, address, options, shipTo = false) + def add_address(xml, creditcard, address, options, shipTo = false) + requires!(options, :email) + xml.tag! shipTo ? 'shipTo' : 'billTo' do - xml.tag! 'firstName', creditcard.first_name - xml.tag! 'lastName', creditcard.last_name - xml.tag! 'street1', address[:address1] - xml.tag! 'street2', address[:address2] - xml.tag! 'city', address[:city] - xml.tag! 'state', address[:state] - xml.tag! 'postalCode', address[:zip] - xml.tag! 'country', address[:country] - xml.tag! 'email', options[:email] + xml.tag! 'firstName', creditcard.first_name if creditcard + xml.tag! 'lastName', creditcard.last_name if creditcard + xml.tag! 'street1', address[:address1] + xml.tag! 'street2', address[:address2] unless address[:address2].blank? + xml.tag! 'city', address[:city] + xml.tag! 'state', address[:state] + xml.tag! 'postalCode', address[:zip] + xml.tag! 'country', address[:country] + xml.tag! 'company', address[:company] unless address[:company].blank? + xml.tag! 'companyTaxID', address[:companyTaxID] unless address[:company_tax_id].blank? + xml.tag! 'phoneNumber', address[:phone_number] unless address[:phone_number].blank? + xml.tag! 'email', options[:email] + xml.tag! 'driversLicenseNumber', options[:drivers_license_number] unless options[:drivers_license_number].blank? + xml.tag! 'driversLicenseState', options[:drivers_license_state] unless options[:drivers_license_state].blank? end end def add_creditcard(xml, creditcard) xml.tag! 'card' do @@ -327,11 +401,11 @@ def add_purchase_service(xml, options) xml.tag! 'ccAuthService', {'run' => 'true'} xml.tag! 'ccCaptureService', {'run' => 'true'} end - + def add_void_service(xml, request_id, request_token) xml.tag! 'voidService', {'run' => 'true'} do xml.tag! 'voidRequestID', request_id xml.tag! 'voidRequestToken', request_token end @@ -349,10 +423,64 @@ xml.tag! 'captureRequestID', request_id xml.tag! 'captureRequestToken', request_token end end + def add_subscription_create_service(xml, options) + xml.tag! 'paySubscriptionCreateService', {'run' => 'true'} + end + + def add_subscription_update_service(xml, options) + xml.tag! 'paySubscriptionUpdateService', {'run' => 'true'} + end + + def add_subscription_delete_service(xml, options) + xml.tag! 'paySubscriptionDeleteService', {'run' => 'true'} + end + + def add_subscription_retrieve_service(xml, options) + xml.tag! 'paySubscriptionRetrieveService', {'run' => 'true'} + end + + def add_subscription(xml, options, reference = nil) + options[:subscription] ||= {} + + xml.tag! 'recurringSubscriptionInfo' do + if reference + reference_code, subscription_id, request_token = reference.split(";") + xml.tag! 'subscriptionID', subscription_id + end + + xml.tag! 'status', options[:subscription][:status] if options[:subscription][:status] + xml.tag! 'amount', options[:subscription][:amount] if options[:subscription][:amount] + xml.tag! 'numberOfPayments', options[:subscription][:occurrences] if options[:subscription][:occurrences] + xml.tag! 'automaticRenew', options[:subscription][:automatic_renew] if options[:subscription][:automatic_renew] + xml.tag! 'frequency', options[:subscription][:frequency] if options[:subscription][:frequency] + xml.tag! 'startDate', options[:subscription][:start_date].strftime("%Y%m%d") if options[:subscription][:start_date] + xml.tag! 'endDate', options[:subscription][:end_date].strftime("%Y%m%d") if options[:subscription][:end_date] + xml.tag! 'approvalRequired', options[:subscription][:approval_required] || false + xml.tag! 'event', options[:subscription][:event] if options[:subscription][:event] + xml.tag! 'billPayment', options[:subscription][:bill_payment] if options[:subscription][:bill_payment] + end + end + + def add_creditcard_payment_method(xml) + xml.tag! 'subscription' do + xml.tag! 'paymentMethod', "credit card" + end + end + + def add_creditcard_or_subscription(xml, money, creditcard_or_reference, options) + if creditcard_or_reference.is_a?(String) + add_purchase_data(xml, money, true, options) + add_subscription(xml, options, creditcard_or_reference) + else + add_address(xml, creditcard_or_reference, options[:billing_address], options) + add_purchase_data(xml, money, true, options) + add_creditcard(xml, creditcard_or_reference) + end + end # Where we actually build the full SOAP request using builder def build_request(body, options) xml = Builder::XmlMarkup.new :indent => 2 xml.instruct! @@ -364,10 +492,10 @@ xml.tag! 'wsse:Password', @options[:password], 'Type' => 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText' end end end xml.tag! 's:Body', {'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema'} do - xml.tag! 'requestMessage', {'xmlns' => 'urn:schemas-cybersource-com:transaction-data-1.32'} do + xml.tag! 'requestMessage', {'xmlns' => "urn:schemas-cybersource-com:transaction-data-#{XSD_VERSION}"} do add_merchant_data(xml, options) xml << body end end end