lib/active_merchant/billing/gateways/payflow.rb in activemerchant-1.1.0 vs lib/active_merchant/billing/gateways/payflow.rb in activemerchant-1.2.0
- old
+ new
@@ -1,82 +1,151 @@
require File.dirname(__FILE__) + '/payflow/payflow_common_api'
+require File.dirname(__FILE__) + '/payflow/payflow_response'
+require File.dirname(__FILE__) + '/payflow_express'
module ActiveMerchant #:nodoc:
module Billing #:nodoc:
class PayflowGateway < Gateway
include PayflowCommonAPI
- def authorize(money, credit_card, options = {})
- if result = test_result_from_cc_number(credit_card.number)
- return result
- end
-
- request = build_sale_or_authorization_request('Authorization', money, credit_card, options)
+ RECURRING_ACTIONS = Set.new([:add, :modify, :cancel, :inquiry, :reactivate, :payment])
+
+ self.supported_cardtypes = [:visa, :master, :american_express, :jcb, :discover, :diners_club]
+ self.homepage_url = 'https://www.paypal.com/cgi-bin/webscr?cmd=_payflow-pro-overview-outside'
+ self.display_name = 'PayPal Payflow Pro'
+
+ def authorize(money, credit_card_or_reference, options = {})
+ request = build_sale_or_authorization_request(:authorization, money, credit_card_or_reference, options)
+
commit(request)
end
- def purchase(money, credit_card, options = {})
- if result = test_result_from_cc_number(credit_card.number)
- return result
+ def purchase(money, credit_card_or_reference, options = {})
+ request = build_sale_or_authorization_request(:purchase, money, credit_card_or_reference, options)
+
+ commit(request)
+ end
+
+ def credit(money, identification_or_credit_card, options = {})
+ if identification_or_credit_card.is_a?(String)
+ # Perform referenced credit
+ request = build_reference_request(:credit, money, identification_or_credit_card, options)
+ else
+ # Perform non-referenced credit
+ request = build_credit_card_request(:credit, money, identification_or_credit_card, options)
end
- request = build_sale_or_authorization_request('Sale', money, credit_card, options)
commit(request)
- end
+ end
+ # Adds or modifies a recurring Payflow profile. See the Payflow Pro Recurring Billing Guide for more details:
+ # https://www.paypal.com/en_US/pdf/PayflowPro_RecurringBilling_Guide.pdf
+ #
+ # Several options are available to customize the recurring profile:
+ #
+ # * <tt>profile_id</tt> - is only required for editing a recurring profile
+ # * <tt>starting_at</tt> - takes a Date, Time, or string in mmddyyyy format. The date must be in the future.
+ # * <tt>name</tt> - The name of the customer to be billed. If not specified, the name from the credit card is used.
+ # * <tt>periodicity</tt> - The frequency that the recurring payments will occur at. Can be one of
+ # :bimonthly, :monthly, :biweekly, :weekly, :yearly, :daily, :semimonthly, :quadweekly, :quarterly, :semiyearly
+ # * <tt>payments</tt> - The term, or number of payments that will be made
+ # * <tt>comment</tt> - A comment associated with the profile
+ def recurring(money, credit_card, options = {})
+ options[:name] = credit_card.name if options[:name].blank? && credit_card
+ request = build_recurring_request(options[:profile_id] ? :modify : :add, money, options) do |xml|
+ add_credit_card(xml, credit_card) if credit_card
+ end
+ commit(request, :recurring)
+ end
+
+ def cancel_recurring(profile_id)
+ request = build_recurring_request(:cancel, 0, :profile_id => profile_id)
+ commit(request, :recurring)
+ end
+
+ def recurring_inquiry(profile_id)
+ request = build_recurring_request(:inquiry, nil, :profile_id => profile_id)
+ commit(request, :recurring)
+ end
+
def express
@express ||= PayflowExpressGateway.new(@options)
end
- def self.supported_cardtypes
- [:visa, :master, :american_express, :jcb, :discover, :diners_club]
+ private
+ def build_sale_or_authorization_request(action, money, credit_card_or_reference, options)
+ if credit_card_or_reference.is_a?(String)
+ build_reference_sale_or_authorization_request(action, money, credit_card_or_reference, options)
+ else
+ build_credit_card_request(action, money, credit_card_or_reference, options)
+ end
end
- private
- def build_sale_or_authorization_request(action, money, credit_card, options)
+ def build_reference_sale_or_authorization_request(action, money, reference, options)
xml = Builder::XmlMarkup.new :indent => 2
- xml.tag! action do
+ xml.tag! TRANSACTIONS[action] do
xml.tag! 'PayData' do
xml.tag! 'Invoice' do
+ xml.tag! 'TotalAmt', amount(money), 'Currency' => options[:currency] || currency(money)
+ end
+ xml.tag! 'Tender' do
+ xml.tag! 'Card' do
+ xml.tag! 'ExtData', 'Name' => 'ORIGID', 'Value' => reference
+ end
+ end
+ end
+ end
+ xml.target!
+ end
+
+ def build_credit_card_request(action, money, credit_card, options)
+ xml = Builder::XmlMarkup.new :indent => 2
+ xml.tag! TRANSACTIONS[action] do
+ xml.tag! 'PayData' do
+ xml.tag! 'Invoice' do
xml.tag! 'CustIP', options[:ip] unless options[:ip].blank?
xml.tag! 'InvNum', options[:order_id] unless options[:order_id].blank?
xml.tag! 'Description', options[:description] unless options[:description].blank?
billing_address = options[:billing_address] || options[:address]
shipping_address = options[:shipping_address] || billing_address
add_address(xml, 'BillTo', billing_address, options)
add_address(xml, 'ShipTo', shipping_address, options)
- xml.tag! 'TotalAmt', amount(money), 'Currency' => currency(money)
-
-
+ xml.tag! 'TotalAmt', amount(money), 'Currency' => options[:currency] || currency(money)
end
xml.tag! 'Tender' do
add_credit_card(xml, credit_card)
end
end
end
xml.target!
end
-
+
def add_credit_card(xml, credit_card)
xml.tag! 'Card' do
- xml.tag! 'CardType', CARD_MAPPING[credit_card.type.to_sym]
+ xml.tag! 'CardType', credit_card_type(credit_card)
xml.tag! 'CardNum', credit_card.number
xml.tag! 'ExpDate', expdate(credit_card)
xml.tag! 'NameOnCard', credit_card.name
xml.tag! 'CVNum', credit_card.verification_value if credit_card.verification_value?
- if [ 'switch', 'solo' ].include?(credit_card.type.to_s)
+ if requires_start_date_or_issue_number?(credit_card)
xml.tag!('ExtData', 'Name' => 'CardStart', 'Value' => startdate(credit_card)) unless credit_card.start_month.blank? || credit_card.start_year.blank?
- xml.tag!('ExtData', 'Name' => 'CardIssue', 'Value' => credit_card.issue_number) unless credit_card.issue_number.blank?
+ xml.tag!('ExtData', 'Name' => 'CardIssue', 'Value' => format(credit_card.issue_number, :two_digits)) unless credit_card.issue_number.blank?
end
end
end
+ def credit_card_type(credit_card)
+ return '' if credit_card.type.blank?
+
+ CARD_MAPPING[credit_card.type.to_sym]
+ end
+
def expdate(creditcard)
year = sprintf("%.4i", creditcard.year)
month = sprintf("%.2i", creditcard.month)
"#{year}#{month}"
@@ -87,11 +156,80 @@
month = format(creditcard.start_month, :two_digits)
"#{month}#{year}"
end
- def build_response(success, message, response, options = {})
- Response.new(success, message, response, options)
+ def build_recurring_request(action, money, options)
+ unless RECURRING_ACTIONS.include?(action)
+ raise StandardError, "Invalid Recurring Profile Action: #{action}"
+ end
+
+ xml = Builder::XmlMarkup.new :indent => 2
+ xml.tag! 'RecurringProfiles' do
+ xml.tag! 'RecurringProfile' do
+ xml.tag! action.to_s.capitalize do
+ unless [:cancel, :inquiry].include?(action)
+ xml.tag! 'RPData' do
+ xml.tag! 'Name', options[:name] unless options[:name].nil?
+ xml.tag! 'TotalAmt', amount(money), 'Currency' => options[:currency] || currency(money)
+ xml.tag! 'PayPeriod', get_pay_period(options)
+ xml.tag! 'Term', options[:payments] unless options[:payments].nil?
+ xml.tag! 'Comment', options[:comment] unless options[:comment].nil?
+
+
+ if initial_tx = options[:initial_transaction]
+ requires!(initial_tx, [:type, :authorization, :purchase])
+ requires!(initial_tx, :amount) if initial_tx[:type] == :purchase
+
+ xml.tag! 'OptionalTrans', TRANSACTIONS[initial_tx[:type]]
+ xml.tag! 'OptionalTransAmt', amount(initial_tx[:amount]) unless initial_tx[:amount].blank?
+ end
+
+ xml.tag! 'Start', format_rp_date(options[:starting_at] || Date.today + 1 )
+ xml.tag! 'EMail', options[:email] unless options[:email].nil?
+
+ billing_address = options[:billing_address] || options[:address]
+ shipping_address = options[:shipping_address] || billing_address
+
+ add_address(xml, 'BillTo', billing_address, options)
+ add_address(xml, 'ShipTo', shipping_address, options)
+ end
+ xml.tag! 'Tender' do
+ yield xml
+ end
+ end
+ if action != :add
+ xml.tag! "ProfileID", options[:profile_id]
+ end
+ end
+ end
+ end
+ end
+
+ def get_pay_period(options)
+ requires!(options, [:periodicity, :bimonthly, :monthly, :biweekly, :weekly, :yearly, :daily, :semimonthly, :quadweekly, :quarterly, :semiyearly])
+ case options[:periodicity]
+ when :weekly then 'Weekly'
+ when :biweekly then 'Bi-weekly'
+ when :semimonthly then 'Semi-monthly'
+ when :quadweekly then 'Every four weeks'
+ when :monthly then 'Monthly'
+ when :quarterly then 'Quarterly'
+ when :semiyearly then 'Semi-yearly'
+ when :yearly then 'Yearly'
+ end
+ end
+
+ def format_rp_date(time)
+ case time
+ when Time, Date then time.strftime("%m%d%Y")
+ else
+ time.to_s
+ end
+ end
+
+ def build_response(success, message, response, options = {})
+ PayflowResponse.new(success, message, response, options)
end
end
end
end