class ActiveMerchant::Billing::LinkpointGateway
Initialization Options :login Your store number :pem The text of your linkpoint PEM file. Note
this is not the path to file, but its contents. If you are only using one PEM file on your site you can declare it globally and then you won't need to include this option
A valid store number is required. Unfortunately, with LinkPoint YOU CAN'T JUST USE ANY OLD STORE NUMBER. Also, you can't just generate your own PEM file. You'll need to use a special PEM file provided by LinkPoint.
Go to www.linkpoint.com/support/sup_teststore.asp to set up a test account and obtain your PEM file.
Declaring PEM file Globally ActiveMerchant::Billing::LinkpointGateway.pem_file = File.read( File.dirname(__FILE__) + '/../mycert.pem' )
Valid Order Options :result =>
LIVE Production mode GOOD Approved response in test mode DECLINE Declined response in test mode DUPLICATE Duplicate response in test mode
:ponumber Order number
:transactionorigin => Source of the transaction
ECI Email or Internet MAIL Mail order MOTO Mail order/Telephone TELEPHONE Telephone RETAIL Face-to-face
:ordertype =>
SALE Real live sale PREAUTH Authorize only POSTAUTH Forced Ticket or Ticket Only transaction VOID CREDIT CALCSHIPPING For shipping charges calculations CALCTAX For sales tax calculations
Recurring Options :action =>
SUBMIT MODIFY CANCEL
:installments Identifies how many recurring payments to charge the customer :startdate Date to begin charging the recurring payments. Format: YYYYMMDD or “immediate” :periodicity =>
MONTHLY BIMONTHLY WEEKLY BIWEEKLY YEARLY DAILY
:threshold Tells how many times to retry the transaction (if it fails) before contacting the merchant. :comments Uh… comments
For reference:
www.linkpointcentral.com/lpc/docs/Help/APIHelp/lpintguide.htm
Entities = { :payment => [:subtotal, :tax, :vattax, :shipping, :chargetotal], :billing => [:name, :address1, :address2, :city, :state, :zip, :country, :email, :phone, :fax, :addrnum], :shipping => [:name, :address1, :address2, :city, :state, :zip, :country, :weight, :items, :carrier, :total], :creditcard => [:cardnumber, :cardexpmonth, :cardexpyear, :cvmvalue, :track], :telecheck => [:routing, :account, :checknumber, :bankname, :bankstate, :dl, :dlstate, :void, :accounttype, :ssn], :transactiondetails => [:transactionorigin, :oid, :ponumber, :taxexempt, :terminaltype, :ip, :reference_number, :recurring, :tdate], :periodic => [:action, :installments, :threshold, :startdate, :periodicity, :comments], :notes => [:comments, :referred] :items => [:item => [:price, :quantity, :description, :id, :options => [:option => [:name, :value]]]] }
LinkPoint's Items entity is an optional entity that can be attached to orders. It is entered as :line_items to be consistent with the CyberSource implementation
The line_item hash goes in the options hash and should look like
:line_items => [ { :id => '123456', :description => 'Logo T-Shirt', :price => '12.00', :quantity => '1', :options => [ { :name => 'Color', :value => 'Red' }, { :name => 'Size', :value => 'XL' } ] }, { :id => '111', :description => 'keychain', :price => '3.00', :quantity => '1' } ]
This functionality is only supported by this particular gateway may be changed at any time
Public Class Methods
# File lib/active_merchant/billing/gateways/linkpoint.rb, line 137 def initialize(options = {}) requires!(options, :login) @options = { :result => 'LIVE', :pem => LinkpointGateway.pem_file }.update(options) raise ArgumentError, "You need to pass in your pem file using the :pem parameter or set it globally using ActiveMerchant::Billing::LinkpointGateway.pem_file = File.read( File.dirname(__FILE__) + '/../mycert.pem' ) or similar" if @options[:pem].blank? @options[:pem].strip! end
Public Instance Methods
Post an authorization.
Captures the funds from an authorized transaction. Order_id must be a valid order id from a prior authorized transaction.
# File lib/active_merchant/billing/gateways/linkpoint.rb, line 214 def capture(money, authorization, options = {}) options.update( :order_id => authorization, :ordertype => 'POSTAUTH' ) commit(money, nil, options) end
# File lib/active_merchant/billing/gateways/linkpoint.rb, line 244 def credit(money, identification, options = {}) ActiveMerchant.deprecated CREDIT_DEPRECATION_MESSAGE refund(money, identification, options) end
Buy the thing
# File lib/active_merchant/billing/gateways/linkpoint.rb, line 187 def purchase(money, creditcard, options={}) requires!(options, :order_id) options.update( :ordertype => 'SALE' ) commit(money, creditcard, options) end
Send a purchase request with periodic options Recurring Options :action =>
SUBMIT MODIFY CANCEL
:installments Identifies how many recurring payments to charge the customer :startdate Date to begin charging the recurring payments. Format: YYYYMMDD or “immediate” :periodicity =>
:monthly :bimonthly :weekly :biweekly :yearly :daily
:threshold Tells how many times to retry the transaction (if it fails) before contacting the merchant. :comments Uh… comments
# File lib/active_merchant/billing/gateways/linkpoint.rb, line 169 def recurring(money, creditcard, options={}) ActiveMerchant.deprecated RECURRING_DEPRECATION_MESSAGE requires!(options, [:periodicity, :bimonthly, :monthly, :biweekly, :weekly, :yearly, :daily], :installments, :order_id) options.update( :ordertype => 'SALE', :action => options[:action] || 'SUBMIT', :installments => options[:installments] || 12, :startdate => options[:startdate] || 'immediate', :periodicity => options[:periodicity].to_s || 'monthly', :comments => options[:comments] || nil, :threshold => options[:threshold] || 3 ) commit(money, creditcard, options) end
Refund an order
identification must be a valid order id previously submitted by SALE
# File lib/active_merchant/billing/gateways/linkpoint.rb, line 236 def refund(money, identification, options = {}) options.update( :ordertype => 'CREDIT', :order_id => identification ) commit(money, nil, options) end
# File lib/active_merchant/billing/gateways/linkpoint.rb, line 253 def scrub(transcript) transcript. gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]'). gsub(%r((<cardnumber>)\d+(</cardnumber>))i, '\1[FILTERED]\2'). gsub(%r((<cvmvalue>)\d+(</cvmvalue>))i, '\1[FILTERED]\2') end
# File lib/active_merchant/billing/gateways/linkpoint.rb, line 249 def supports_scrubbing true end
Void a previous transaction
# File lib/active_merchant/billing/gateways/linkpoint.rb, line 223 def void(identification, options = {}) options.update( :order_id => identification, :ordertype => 'VOID' ) commit(nil, nil, options) end
Private Instance Methods
adds LinkPoint's Items entity to the XML. Called from post_data
# File lib/active_merchant/billing/gateways/linkpoint.rb, line 306 def build_items(element, items) for item in items item_element = element.add_element('item') for key, value in item if key == :options options_element = item_element.add_element('options') for option in value opt_element = options_element.add_element('option') opt_element.add_element('name').text = option[:name] unless option[:name].blank? opt_element.add_element('value').text = option[:value] unless option[:value].blank? end else item_element.add_element(key.to_s).text = item[key].to_s unless item[key].blank? end end end end
Commit the transaction by posting the XML file to the LinkPoint server
# File lib/active_merchant/billing/gateways/linkpoint.rb, line 263 def commit(money, creditcard, options = {}) response = parse(ssl_post(test? ? self.test_url : self.live_url, post_data(money, creditcard, options))) Response.new(successful?(response), response[:message], response, :test => test?, :authorization => response[:ordernum], :avs_result => { :code => response[:avs].to_s[2, 1] }, :cvv_result => response[:avs].to_s[3, 1] ) end
# File lib/active_merchant/billing/gateways/linkpoint.rb, line 444 def format_creditcard_expiry_year(year) sprintf('%.4i', year)[-2..-1] end
Set up the parameters hash just once so we don't have to do it for every action.
# File lib/active_merchant/billing/gateways/linkpoint.rb, line 326 def parameters(money, creditcard, options = {}) params = { :payment => { :subtotal => amount(options[:subtotal]), :tax => amount(options[:tax]), :vattax => amount(options[:vattax]), :shipping => amount(options[:shipping]), :chargetotal => amount(money) }, :transactiondetails => { :transactionorigin => options[:transactionorigin] || 'ECI', :oid => options[:order_id], :ponumber => options[:ponumber], :taxexempt => options[:taxexempt], :terminaltype => options[:terminaltype], :ip => options[:ip], :reference_number => options[:reference_number], :recurring => options[:recurring] || 'NO', # DO NOT USE if you are using the periodic billing option. :tdate => options[:tdate] }, :orderoptions => { :ordertype => options[:ordertype], :result => @options[:result] }, :periodic => { :action => options[:action], :installments => options[:installments], :threshold => options[:threshold], :startdate => options[:startdate], :periodicity => options[:periodicity], :comments => options[:comments] }, :telecheck => { :routing => options[:telecheck_routing], :account => options[:telecheck_account], :checknumber => options[:telecheck_checknumber], :bankname => options[:telecheck_bankname], :dl => options[:telecheck_dl], :dlstate => options[:telecheck_dlstate], :void => options[:telecheck_void], :accounttype => options[:telecheck_accounttype], :ssn => options[:telecheck_ssn], } } if creditcard params[:creditcard] = { :cardnumber => creditcard.number, :cardexpmonth => creditcard.month, :cardexpyear => format_creditcard_expiry_year(creditcard.year), :track => nil } if creditcard.verification_value? params[:creditcard][:cvmvalue] = creditcard.verification_value params[:creditcard][:cvmindicator] = 'provided' else params[:creditcard][:cvmindicator] = 'not_provided' end end if billing_address = options[:billing_address] || options[:address] params[:billing] = {} params[:billing][:name] = billing_address[:name] || (creditcard ? creditcard.name : nil) params[:billing][:address1] = billing_address[:address1] unless billing_address[:address1].blank? params[:billing][:address2] = billing_address[:address2] unless billing_address[:address2].blank? params[:billing][:city] = billing_address[:city] unless billing_address[:city].blank? params[:billing][:state] = billing_address[:state] unless billing_address[:state].blank? params[:billing][:zip] = billing_address[:zip] unless billing_address[:zip].blank? params[:billing][:country] = billing_address[:country] unless billing_address[:country].blank? params[:billing][:company] = billing_address[:company] unless billing_address[:company].blank? params[:billing][:phone] = billing_address[:phone] unless billing_address[:phone].blank? params[:billing][:email] = options[:email] unless options[:email].blank? end if shipping_address = options[:shipping_address] params[:shipping] = {} params[:shipping][:name] = shipping_address[:name] || (creditcard ? creditcard.name : nil) params[:shipping][:address1] = shipping_address[:address1] unless shipping_address[:address1].blank? params[:shipping][:address2] = shipping_address[:address2] unless shipping_address[:address2].blank? params[:shipping][:city] = shipping_address[:city] unless shipping_address[:city].blank? params[:shipping][:state] = shipping_address[:state] unless shipping_address[:state].blank? params[:shipping][:zip] = shipping_address[:zip] unless shipping_address[:zip].blank? params[:shipping][:country] = shipping_address[:country] unless shipping_address[:country].blank? end params[:items] = options[:line_items] if options[:line_items] return params end
# File lib/active_merchant/billing/gateways/linkpoint.rb, line 419 def parse(xml) # For reference, a typical response... # <r_csp></r_csp> # <r_time></r_time> # <r_ref></r_ref> # <r_error></r_error> # <r_ordernum></r_ordernum> # <r_message>This is a test transaction and will not show up in the Reports</r_message> # <r_code></r_code> # <r_tdate>Thu Feb 2 15:40:21 2006</r_tdate> # <r_score></r_score> # <r_authresponse></r_authresponse> # <r_approved>APPROVED</r_approved> # <r_avs></r_avs> response = {:message => 'Global Error Receipt', :complete => false} xml = REXML::Document.new("<response>#{xml}</response>") xml.root&.elements&.each do |node| response[node.name.downcase.sub(/^r_/, '').to_sym] = normalize(node.text) end response end
Build the XML file
# File lib/active_merchant/billing/gateways/linkpoint.rb, line 279 def post_data(money, creditcard, options) params = parameters(money, creditcard, options) xml = REXML::Document.new order = xml.add_element('order') # Merchant Info merchantinfo = order.add_element('merchantinfo') merchantinfo.add_element('configfile').text = @options[:login] # Loop over the params hash to construct the XML string for key, value in params elem = order.add_element(key.to_s) if key == :items build_items(elem, value) else for k, _ in params[key] elem.add_element(k.to_s).text = params[key][k].to_s if params[key][k] end end # Linkpoint doesn't understand empty elements: order.delete(elem) if elem.size == 0 end return xml.to_s end
# File lib/active_merchant/billing/gateways/linkpoint.rb, line 274 def successful?(response) response[:approved] == 'APPROVED' end