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