require 'net/http' require 'net/https' require 'uri' require 'active_support/core_ext/hash/indifferent_access' class Hash include ActiveSupport::CoreExtensions::Hash::IndifferentAccess end # TrustCommerce (http://www.trustcommerce.com) is a payment gateway providing credit card # processing and subscription/recurring billing services. # # TrustCommerce implements recurring billing through a service named Citadel # (http://www.trustcommerce.com/citadel.php). # # Merchants create a billing profile through Citadel including customer information, # credit card data, and billing frequency. Citadel stores the information and issues the # merchant a Billing ID. A Billing ID is a six-character alphanumeric string identifying # the customer profile. # # Merchants can modify the customer's information, credit card data, or billing frequency # anytime using the issued Billing ID. # # This plugin provides a simple interface to create, edit, and delete subscriptions # using the TrustCommerce Citadel API. # # It is recommended to download and install the TCLink ruby extension # (http://www.trustcommerce.com/tclink.php). # This extension provides failover capability and enhanced security features. # If this library is not installed, standard POST over SSL will be used. class TrustCommerceGateway # When you signup for a TrustCommerce account you are issued a custid and a password. # These are your credentials when using the Citadel API. # # IMPORTANT NOTE REGARDING PASSWORDS # The password that TrustCommerce issues never changes or expires when used through the tclink # extension. However if you choose to use ssl over http instead (the fallback option if the tclink # library is not installed), be aware that you need to set the password to your vault password. # Likewise, if your application uses the query() method you must set the vault_password. # The reason is that TrustCommerce currently routes these query() calls through the vault # and therefore your password must be set accordingly. To make matters more complicated, # TrustCommerce currently forces you to change the vault password every 90 days. # # Override these default values with your issued credentials in your app's environment.rb # # environment.rb # ... # TrustCommerceGateway.custid = 'your_custid' # TrustCommerceGateway.password = 'your_password' # # # (optional) Sets vault password for use in query() calls # TrustCommerceGateway.vault_password = 'your_vault_password' # ... class << self attr_accessor :custid attr_accessor :password attr_accessor :vault_password def vault_password @vault_password || password end end self.custid = 'TestMerchant' self.password = 'password' # Settings for standard POST over SSL # Only used if TCLink library is not installed API_SETTINGS = { :domain => 'vault.trustcommerce.com', :query_path => '/query/', :trans_path => '/trans/', :port => 443 } class Subscription # Creates a customer profile and returns a hash. # # response = TrustCommerceGateway::Subscription.create( # :cc => '4111111111111111', # :exp => '0412', # :name => 'Jennifer Smith', # :amount => 1200, # :cycle => '1m', # :demo => 'y' # ) # # if response['status'] == 'approved' # puts "Customer profile created with Billing ID: #{response['billingid']}" # else # puts "An error occurred: #{response['error']}" # end def self.create(options) return TrustCommerceGateway.send_request(options.merge(:action => 'store')) end # Updates a customer profile and returns a hash. # # response = TrustCommerceGateway::Subscription.update( # :billingid => create_response['billingid'], # :cc => '5411111111111115', # :exp => '0412' # ) # # if response['status'] == 'accepted' # puts "Customer profile updated" # else # puts "An error occurred: #{response['error']}" # end def self.update(options) return TrustCommerceGateway.send_request(options.merge(:action => 'store')) end # Removes a customer profile from active use and returns a hash. # # response = TrustCommerceGateway::Subscription.destroy( # :billingid => '123456' # ) # # if response['status'] == 'accepted' # puts 'Customer profile removed from active use' # else # puts 'An error occurred' # end def self.destroy(options) return TrustCommerceGateway.send_request(options.merge(:action => 'unstore')) end # Process a one-time sale against a Billing ID # # response = TrustCommerceGateway::Subscription.charge( # :billingid => 'ABC123', # :amount => 1995, # ) def self.charge(options) return TrustCommerceGateway.send_request(options.merge(:action => 'sale')) end # Process a credit against a transaction # # response = TrustCommerceGateway::Subscription.credit( # :transid => '001-0000111101', # :amount => 1995, # ) def self.credit(options) return TrustCommerceGateway.send_request(options.merge(:action => 'credit')) end # Query the Vault API # # More information at https://vault.trustcommerce.com/downloads/TCDevGuide.html#query # # # returns list of sale transactions in CSV format for customer ABC123 # query_response = TrustCommerceGateway::Subscription.query( # :querytype => 'transaction', # :action => 'sale', # :billingid => 'ABC123' # ) def self.query(options) return TrustCommerceGateway.send_query(options) end end # Checks if TCLink library (http://www.trustcommerce.com/tclink.php) is installed. def self.tclink? begin require 'tclink' true rescue LoadError false end end private def self.clean_parameters(hash) hash.keys.inject({}) { |h,k| h[k.to_s] = hash[k].to_s; h } end def self.send_request(options) options[:custid] = self.custid options[:password] = self.password options.update(:demo => 'y') if ENV['RAILS_ENV'] != 'production' parameters = clean_parameters(options) if tclink? # use TCLink extension if installed return TCLink.send(parameters).with_indifferent_access else # TCLink library not installed - use https post parameters[:password] = self.vault_password.to_s response = send_https_request(API_SETTINGS[:trans_path], parameters) # --- [ parse response ] --- results = {} lines = response.body.split("\n") lines.each do |line| k, v = line.split('=') results[k] = v end return results.with_indifferent_access end end def self.send_query(options) options[:custid] = self.custid options[:password] = self.vault_password.to_s response = send_https_request(API_SETTINGS[:query_path], clean_parameters(options)) end def self.send_https_request(path, parameters) http = Net::HTTP.new(API_SETTINGS[:domain], API_SETTINGS[:port]) http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_NONE # avoid ssl cert warning request = Net::HTTP::Post.new(path) request.form_data = parameters http.request(request) end end