activemerchant 1.8.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. data.tar.gz.sig +0 -0
  2. data/CHANGELOG +9 -0
  3. data/CONTRIBUTORS +21 -1
  4. data/README.rdoc +16 -4
  5. data/lib/active_merchant.rb +2 -0
  6. data/lib/active_merchant/billing/gateways/inspire.rb +221 -0
  7. data/lib/active_merchant/billing/gateways/paybox_direct.rb +205 -0
  8. data/lib/active_merchant/billing/gateways/secure_net.rb +330 -0
  9. data/lib/active_merchant/billing/integrations/bogus.rb +1 -1
  10. data/lib/active_merchant/billing/integrations/chronopay.rb +1 -1
  11. data/lib/active_merchant/billing/integrations/direc_pay.rb +37 -0
  12. data/lib/active_merchant/billing/integrations/direc_pay/helper.rb +188 -0
  13. data/lib/active_merchant/billing/integrations/direc_pay/notification.rb +76 -0
  14. data/lib/active_merchant/billing/integrations/direc_pay/return.rb +32 -0
  15. data/lib/active_merchant/billing/integrations/direc_pay/status.rb +37 -0
  16. data/lib/active_merchant/billing/integrations/gestpay.rb +1 -1
  17. data/lib/active_merchant/billing/integrations/helper.rb +8 -5
  18. data/lib/active_merchant/billing/integrations/hi_trust.rb +1 -1
  19. data/lib/active_merchant/billing/integrations/nochex.rb +1 -1
  20. data/lib/active_merchant/billing/integrations/paypal.rb +1 -1
  21. data/lib/active_merchant/billing/integrations/return.rb +4 -2
  22. data/lib/active_merchant/billing/integrations/sage_pay_form.rb +37 -0
  23. data/lib/active_merchant/billing/integrations/sage_pay_form/encryption.rb +33 -0
  24. data/lib/active_merchant/billing/integrations/sage_pay_form/helper.rb +109 -0
  25. data/lib/active_merchant/billing/integrations/sage_pay_form/notification.rb +204 -0
  26. data/lib/active_merchant/billing/integrations/sage_pay_form/return.rb +27 -0
  27. data/lib/active_merchant/billing/integrations/two_checkout.rb +1 -1
  28. data/lib/active_merchant/version.rb +1 -1
  29. metadata +17 -4
  30. metadata.gz.sig +0 -0
data.tar.gz.sig CHANGED
Binary file
data/CHANGELOG CHANGED
@@ -1,5 +1,14 @@
1
1
  = ActiveMerchant CHANGELOG
2
2
 
3
+ == Version 1.9.0 (October 14, 2010)
4
+
5
+ * Add support for DirecPay gateway [Soleone]
6
+ * Add SagePay Form integration gateway [Adrian Irving-Beer]
7
+ * Allow Return class to include a Notification for gateways that treat the direct response as a notification [Adrian Irving-Beer]
8
+ * Add support for PayboxDirect gateway [Donald Piret]
9
+ * Add support for SecureNet gateway [Kal]
10
+ * Add support for the Inspire gateway [ryan r. smith]
11
+
3
12
  == Version 1.8.0 (September 24, 2010)
4
13
 
5
14
  * PayPal Express: Add support for billing agreements [Nathaniel Talbott]
@@ -147,4 +147,24 @@ Garanti (May 05, 2010)
147
147
 
148
148
  Braintree Blue Gateway (May 19th, 2010)
149
149
 
150
- * Braintree (code@getbraintree.com)
150
+ * Braintree (code@getbraintree.com)
151
+
152
+ Inspire Gateway (September 27, 2010)
153
+
154
+ * ryan r. smith
155
+
156
+ SecureNet Gateway (September 27, 2010)
157
+
158
+ * Kal
159
+
160
+ PayboxDirect Gateway (September 27, 2010)
161
+
162
+ * Donald Piret <donald@donaldpiret.com>
163
+
164
+ SagePay Form Offsite Gateway (October 14, 2010)
165
+
166
+ * Adrian Irving-Beer
167
+
168
+ DirecPay Gateway (October 14, 2010)
169
+
170
+ * Soleone
@@ -19,17 +19,26 @@ The {ActiveMerchant Wiki}[http://github.com/Shopify/active_merchant/wikis] conta
19
19
  * {CyberSource}[http://www.cybersource.com] - US
20
20
  * {DataCash}[http://www.datacash.com/] - GB
21
21
  * {Efsnet}[http://www.concordefsnet.com/] - US
22
+ * {Elavon MyVirtualMerchant}[http://www.elavon.com] - US, CA
22
23
  * {eWAY}[http://www.eway.com.au/] - AU
23
24
  * {E-xact}[http://www.e-xact.com] - CA, US
25
+ * {FirstPay}[http://www.first-pay.com] - US
26
+ * {Garanti Sanal POS}[https://ccpos.garanti.com.tr/ccRaporlar/garanti/ccReports] - US, TR
27
+ * {Inspire}[http://www.inspiregateway.com] - US
28
+ * {InstaPay}[http://www.instapayllc.com] - US
24
29
  * {Iridium}[http://www.iridiumcorp.co.uk/] - UK, ES
30
+ * {JetPay}[http://www.jetpay.com] - US
25
31
  * {LinkPoint}[http://www.linkpoint.com/] - US
26
32
  * {Merchant e-Solutions}[http://merchante-solutions.com/] - US
33
+ * {MerchantWare}[http://merchantwarehouse.com/merchantware] - US
27
34
  * {Modern Payments}[http://www.modpay.com] - US
28
35
  * {Moneris}[http://www.moneris.com/] - CA
29
36
  * {Netaxept}[http://www.betalingsterminal.no/Netthandel-forside] - NO, DK, SE, FI
30
37
  * {NetRegistry}[http://www.netregistry.com.au] - AU
31
38
  * {NELiX TransaX Gateway}[http://www.nelixtransax.com] - US
32
39
  * {NETbilling}[http://www.netbilling.com] - US
40
+ * {Ogone DirectLink}[http://www.ogone.com] - BE, DE, FR, NL, AT, CH
41
+ * {PayBox Direct}[http://www.paybox.com] - FR
33
42
  * {PayJunction}[http://www.payjunction.com/] - US
34
43
  * {PaySecure}[http://www.commsecure.com.au/paysecure.shtml] - AU
35
44
  * {PayPal Express Checkout}[https://www.paypal.com/cgi-bin/webscr?cmd=xpt/merchant/ExpressCheckoutIntro-outside] - US, CA, SG, AU
@@ -47,6 +56,7 @@ The {ActiveMerchant Wiki}[http://github.com/Shopify/active_merchant/wikis] conta
47
56
  * {Realex}[http://www.realexpayments.com/] - IE, GB
48
57
  * {Sage Payment Solutions}[http://www.sagepayments.com] - US, CA
49
58
  * {Sallie Mae}[http://www.salliemae.com] - US
59
+ * {SecureNet}[http://www.securenet.com] - US
50
60
  * {SecurePay}[http://securepay.com.au] - AU
51
61
  * {SecurePay}[http://www.securepay.com/] - US
52
62
  * {SecurePayTech}[http://www.securepaytech.com/] - NZ
@@ -60,12 +70,14 @@ The {ActiveMerchant Wiki}[http://github.com/Shopify/active_merchant/wikis] conta
60
70
 
61
71
  == Supported Offsite Payment Gateways
62
72
 
63
- * {PayPal Website Payments Standard}[https://www.paypal.com/cgi-bin/webscr?cmd=_wp-standard-overview-outside]
64
- * {Chronopay}[http://www.chronopay.com]
65
- * {Nochex}[http://www.nochex.com]
66
- * {Banca Sella GestPay}[https://www.sella.it/banca/ecommerce/gestpay/gestpay.jsp]
67
73
  * {2 Checkout}[http://www.2checkout.com]
74
+ * {Banca Sella GestPay}[https://www.sella.it/banca/ecommerce/gestpay/gestpay.jsp]
75
+ * {Chronopay}[http://www.chronopay.com]
76
+ * {DirecPay}[http://www.timesofmoney.com/direcpay/jsp/home.jsp]
68
77
  * {HiTRUST}[http://www.hitrust.com.hk/]
78
+ * {Nochex}[http://www.nochex.com]
79
+ * {PayPal Website Payments Standard}[https://www.paypal.com/cgi-bin/webscr?cmd=_wp-standard-overview-outside]
80
+ * {SagePay Form}[http://www.sagepay.com/products_services/sage_pay_go/integration/form]
69
81
 
70
82
  == Download
71
83
 
@@ -31,6 +31,8 @@ require 'active_support/core_ext/class/attribute_accessors'
31
31
  require 'active_support/core_ext/class/delegating_attributes'
32
32
  require 'active_support/core_ext/module/attribute_accessors'
33
33
  require 'active_support/core_ext/kernel/requires'
34
+ require 'active_support/base64'
35
+ require 'active_support/secure_random'
34
36
 
35
37
  require 'builder'
36
38
  require 'cgi'
@@ -0,0 +1,221 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'check.rb')
2
+ module ActiveMerchant #:nodoc:
3
+ module Billing #:nodoc:
4
+ class InspireGateway < Gateway
5
+ URL = 'https://secure.inspiregateway.net/api/transact.php'
6
+
7
+ self.supported_countries = ['US']
8
+ self.supported_cardtypes = [:visa, :master, :american_express]
9
+ self.homepage_url = 'http://www.inspiregateway.com'
10
+ self.display_name = 'Inspire Commerce'
11
+ # Creates a new InspireGateway
12
+ #
13
+ # The gateway requires that a valid login and password be passed
14
+ # in the +options+ hash.
15
+ #
16
+ # ==== Options
17
+ #
18
+ # * <tt>:login</tt> -- The Inspire Username.
19
+ # * <tt>:password</tt> -- The Inspire Passowrd.
20
+ # See the Inspire Integration Guide for details. (default: +false+)
21
+ def initialize(options = {})
22
+ requires!(options, :login, :password)
23
+ @options = options
24
+ super
25
+ end
26
+
27
+ # Pass :store => true in the options to store the
28
+ # payment info at Inspire Gateway and get a generated
29
+ # customer_vault_id in the response.
30
+ # Pass :store => some_number_or_string to specify the
31
+ # customer_vault_id InspireGateway should use (make sure it's
32
+ # unique).
33
+ def authorize(money, creditcard, options = {})
34
+ post = {}
35
+ add_invoice(post, options)
36
+ add_payment_source(post, creditcard,options)
37
+ add_address(post, creditcard, options)
38
+ add_customer_data(post, options)
39
+
40
+ commit('auth', money, post)
41
+ end
42
+
43
+ def purchase(money, payment_source, options = {})
44
+ post = {}
45
+ add_invoice(post, options)
46
+ add_payment_source(post, payment_source, options)
47
+ add_address(post, payment_source, options)
48
+ add_customer_data(post, options)
49
+
50
+ commit('sale', money, post)
51
+ end
52
+
53
+ def capture(money, authorization, options = {})
54
+ post ={}
55
+ post[:transactionid] = authorization
56
+ commit('capture', money, post)
57
+ end
58
+
59
+ def void(authorization, options = {})
60
+ post ={}
61
+ post[:transactionid] = authorization
62
+ commit('void', nil, post)
63
+ end
64
+
65
+ # Update the values (such as CC expiration) stored at
66
+ # InspireGateway. The CC number must be supplied in the
67
+ # CreditCard object.
68
+ def update(vault_id, creditcard, options = {})
69
+ post = {}
70
+ post[:customer_vault] = "update_customer"
71
+ add_customer_vault_id(post, vault_id)
72
+ add_creditcard(post, creditcard, options)
73
+ add_address(post, creditcard, options)
74
+ add_customer_data(post, options)
75
+
76
+ commit(nil, nil, post)
77
+ end
78
+
79
+ def delete(vault_id)
80
+ post = {}
81
+ post[:customer_vault] = "delete_customer"
82
+ add_customer_vault_id(post, vault_id)
83
+ commit(nil, nil, post)
84
+ end
85
+
86
+ # To match the other stored-value gateways, like TrustCommerce,
87
+ # store and unstore need to be defined
88
+ def store(creditcard, options = {})
89
+ billing_id = options.delete(:billing_id).to_s || true
90
+ authorize(100, creditcard, options.merge(:store => billing_id))
91
+ end
92
+
93
+ alias_method :unstore, :delete
94
+
95
+ private
96
+ def add_customer_data(post, options)
97
+ if options.has_key? :email
98
+ post[:email] = options[:email]
99
+ end
100
+
101
+ if options.has_key? :ip
102
+ post[:ipaddress] = options[:ip]
103
+ end
104
+ end
105
+
106
+ def add_address(post, creditcard, options)
107
+ if address = options[:billing_address] || options[:address]
108
+ post[:address1] = address[:address1].to_s
109
+ post[:address2] = address[:address2].to_s unless address[:address2].blank?
110
+ post[:company] = address[:company].to_s
111
+ post[:phone] = address[:phone].to_s
112
+ post[:zip] = address[:zip].to_s
113
+ post[:city] = address[:city].to_s
114
+ post[:country] = address[:country].to_s
115
+ post[:state] = address[:state].blank? ? 'n/a' : address[:state]
116
+ end
117
+ end
118
+
119
+ def add_invoice(post, options)
120
+ post[:orderid] = options[:order_id].to_s.gsub(/[^\w.]/, '')
121
+ post[:orderdescription] = options[:description]
122
+ end
123
+
124
+ def add_payment_source(params, source, options={})
125
+ case determine_funding_source(source)
126
+ when :vault then add_customer_vault_id(params, source)
127
+ when :credit_card then add_creditcard(params, source, options)
128
+ when :check then add_check(params, source)
129
+ end
130
+ end
131
+
132
+ def add_customer_vault_id(params,vault_id)
133
+ params[:customer_vault_id] = vault_id
134
+ end
135
+
136
+ def add_creditcard(post, creditcard,options)
137
+ if options[:store]
138
+ post[:customer_vault] = "add_customer"
139
+ post[:customer_vault_id] = options[:store] unless options[:store] == true
140
+ end
141
+ post[:ccnumber] = creditcard.number
142
+ post[:cvv] = creditcard.verification_value if creditcard.verification_value?
143
+ post[:ccexp] = expdate(creditcard)
144
+ post[:firstname] = creditcard.first_name
145
+ post[:lastname] = creditcard.last_name
146
+ end
147
+
148
+ def add_check(post, check)
149
+ post[:payment] = 'check' # Set transaction to ACH
150
+ post[:checkname] = check.name # The name on the customer's Checking Account
151
+ post[:checkaba] = check.routing_number # The customer's bank routing number
152
+ post[:checkaccount] = check.account_number # The customer's account number
153
+ post[:account_holder_type] = check.account_holder_type # The customer's type of ACH account
154
+ post[:account_type] = check.account_type # The customer's type of ACH account
155
+ end
156
+
157
+ def parse(body)
158
+ results = {}
159
+ body.split(/&/).each do |pair|
160
+ key,val = pair.split(/=/)
161
+ results[key] = val
162
+ end
163
+
164
+ results
165
+ end
166
+
167
+ def commit(action, money, parameters)
168
+ parameters[:amount] = amount(money) if money
169
+
170
+ response = parse( ssl_post(URL, post_data(action,parameters)) )
171
+
172
+ Response.new(response["response"] == "1", message_from(response), response,
173
+ :authorization => response["transactionid"],
174
+ :test => test?,
175
+ :cvv_result => response["cvvresponse"],
176
+ :avs_result => { :code => response["avsresponse"] }
177
+ )
178
+
179
+ end
180
+
181
+ def expdate(creditcard)
182
+ year = sprintf("%.4i", creditcard.year)
183
+ month = sprintf("%.2i", creditcard.month)
184
+
185
+ "#{month}#{year[-2..-1]}"
186
+ end
187
+
188
+
189
+ def message_from(response)
190
+ case response["responsetext"]
191
+ when "SUCCESS","Approved"
192
+ "This transaction has been approved"
193
+ when "DECLINE"
194
+ "This transaction has been declined"
195
+ else
196
+ response["responsetext"]
197
+ end
198
+ end
199
+
200
+ def post_data(action, parameters = {})
201
+ post = {}
202
+ post[:username] = @options[:login]
203
+ post[:password] = @options[:password]
204
+ post[:type] = action if action
205
+
206
+ request = post.merge(parameters).map {|key,value| "#{key}=#{CGI.escape(value.to_s)}"}.join("&")
207
+ request
208
+ end
209
+
210
+ def determine_funding_source(source)
211
+ case
212
+ when source.is_a?(String) then :vault
213
+ when CreditCard.card_companies.keys.include?(card_brand(source)) then :credit_card
214
+ when card_brand(source) == 'check' then :check
215
+ else raise ArgumentError, "Unsupported funding source provided"
216
+ end
217
+ end
218
+ end
219
+ end
220
+ end
221
+
@@ -0,0 +1,205 @@
1
+ require 'iconv'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+ class PayboxDirectGateway < Gateway
6
+ TEST_URL = 'https://ppps.paybox.com/PPPS.php'
7
+ TEST_URL_BACKUP = 'https://ppps1.paybox.com/PPPS.php'
8
+ LIVE_URL = 'https://ppps.paybox.com/PPPS.php'
9
+ LIVE_URL_BACKUP = 'https://ppps1.paybox.com/PPPS.php'
10
+
11
+ # Payment API Version
12
+ API_VERSION = '00104'
13
+
14
+ # Transactions hash
15
+ TRANSACTIONS = {
16
+ :authorization => '00001',
17
+ :capture => '00002',
18
+ :purchase => '00003',
19
+ :unreferenced_credit => '00004',
20
+ :void => '00005',
21
+ :refund => '00014'
22
+ }
23
+
24
+ CURRENCY_CODES = {
25
+ "AUD"=> '036',
26
+ "CAD"=> '124',
27
+ "CZK"=> '203',
28
+ "DKK"=> '208',
29
+ "HKD"=> '344',
30
+ "ICK"=> '352',
31
+ "JPY"=> '392',
32
+ "NOK"=> '578',
33
+ "SGD"=> '702',
34
+ "SEK"=> '752',
35
+ "CHF"=> '756',
36
+ "GBP"=> '826',
37
+ "USD"=> '840',
38
+ "EUR"=> '978'
39
+ }
40
+
41
+ SUCCESS_CODES = ['00000']
42
+ UNAVAILABILITY_CODES = ['00001', '00097', '00098']
43
+ FRAUD_CODES = ['00102','00104','00105','00134','00138','00141','00143','00156','00157','00159']
44
+ SUCCESS_MESSAGE = 'The transaction was approved'
45
+ FAILURE_MESSAGE = 'The transaction failed'
46
+
47
+ # Money is referenced in cents
48
+ self.money_format = :cents
49
+ self.default_currency = 'EUR'
50
+
51
+ # The countries the gateway supports merchants from as 2 digit ISO country codes
52
+ self.supported_countries = ['FR']
53
+
54
+ # The card types supported by the payment gateway
55
+ self.supported_cardtypes = [:visa, :master, :american_express, :diners_club, :jcb]
56
+
57
+ # The homepage URL of the gateway
58
+ self.homepage_url = 'http://www.paybox.com/'
59
+
60
+ # The name of the gateway
61
+ self.display_name = 'Paybox Direct'
62
+
63
+ def initialize(options = {})
64
+ requires!(options, :login, :password)
65
+ @options = options
66
+ super
67
+ end
68
+
69
+ def authorize(money, creditcard, options = {})
70
+ post = {}
71
+ add_invoice(post, options)
72
+ add_creditcard(post, creditcard)
73
+ commit('authorization', money, post)
74
+ end
75
+
76
+ def purchase(money, creditcard, options = {})
77
+ post = {}
78
+ add_invoice(post, options)
79
+ add_creditcard(post, creditcard)
80
+ commit('purchase', money, post)
81
+ end
82
+
83
+ def capture(money, authorization, options = {})
84
+ requires!(options, :order_id)
85
+ post = {}
86
+ add_invoice(post, options)
87
+ post[:numappel] = authorization[0,10]
88
+ post[:numtrans] = authorization[10,10]
89
+ commit('capture', money, post)
90
+ end
91
+
92
+ def void(identification, options = {})
93
+ requires!(options, :order_id, :amount)
94
+ post ={}
95
+ add_invoice(post, options)
96
+ add_reference(post, identification)
97
+ post[:porteur] = '000000000000000'
98
+ post[:dateval] = '0000'
99
+ commit('void', options[:amount], post)
100
+ end
101
+
102
+ def credit(money, identification, options = {})
103
+ post = {}
104
+ add_invoice(post, options)
105
+ add_reference(post, identification)
106
+ commit('refund', money, post)
107
+ end
108
+
109
+ def test?
110
+ @options[:test] || Base.gateway_mode == :test
111
+ end
112
+
113
+ private
114
+
115
+ def add_invoice(post, options)
116
+ post[:reference] = options[:order_id]
117
+ end
118
+
119
+ def add_creditcard(post, creditcard)
120
+ post[:porteur] = creditcard.number
121
+ post[:dateval] = expdate(creditcard)
122
+ post[:cvv] = creditcard.verification_value if creditcard.verification_value?
123
+ end
124
+
125
+ def add_reference(post, identification)
126
+ post[:numappel] = identification[0,10]
127
+ post[:numtrans] = identification[10,10]
128
+ end
129
+
130
+ def parse(body)
131
+ body = Iconv.iconv("UTF-8","LATIN1", body.to_s).join
132
+ results = {}
133
+ body.split(/&/).each do |pair|
134
+ key,val = pair.split(/\=/)
135
+ results[key.downcase.to_sym] = CGI.unescape(val) if val
136
+ end
137
+ results
138
+ end
139
+
140
+ def commit(action, money = nil, parameters = nil)
141
+ parameters[:montant] = ('0000000000' + (money ? amount(money) : ''))[-10..-1]
142
+ parameters[:devise] = CURRENCY_CODES[options[:currency] || currency(money)]
143
+ request_data = post_data(action,parameters)
144
+ response = parse(ssl_post(test? ? TEST_URL : LIVE_URL, request_data))
145
+ response = parse(ssl_post(test? ? TEST_URL_BACKUP : LIVE_URL_BACKUP, request_data)) if service_unavailable?(response)
146
+ Response.new(success?(response), message_from(response), response.merge(
147
+ :timestamp => parameters[:dateq]),
148
+ :test => test?,
149
+ :authorization => response[:numappel].to_s + response[:numtrans].to_s,
150
+ :cvv_result => '',
151
+ :avs_result => '',
152
+ :fraud_review => fraud_review?(response),
153
+ :sent_params => parameters.delete_if{|key,value| ['porteur','dateval','cvv'].include?(key.to_s)}
154
+ )
155
+ end
156
+
157
+ def success?(response)
158
+ SUCCESS_CODES.include?(response[:codereponse])
159
+ end
160
+
161
+ def fraud_review?(response)
162
+ FRAUD_CODES.include?(response[:codereponse])
163
+ end
164
+
165
+ def service_unavailable?(response)
166
+ UNAVAILABILITY_CODES.include?(response[:codereponse])
167
+ end
168
+
169
+ def message_from(response)
170
+ success?(response) ? SUCCESS_MESSAGE : (response[:commentaire] || FAILURE_MESSAGE)
171
+ end
172
+
173
+ def post_data(action, parameters = {})
174
+
175
+ parameters.update(
176
+ :version => API_VERSION,
177
+ :type => TRANSACTIONS[action.to_sym],
178
+ :dateq => Time.now.strftime('%d%m%Y%H%M%S'),
179
+ :numquestion => unique_id(parameters[:order_id]),
180
+ :site => @options[:login].to_s[0,7],
181
+ :rang => @options[:login].to_s[7..-1],
182
+ :cle => @options[:password],
183
+ :pays => '',
184
+ :archivage => parameters[:order_id]
185
+ )
186
+
187
+ parameters.collect { |key, value| "#{key.to_s.upcase}=#{CGI.escape(value.to_s)}" }.join("&")
188
+ end
189
+
190
+ def unique_id(seed = 0)
191
+ randkey = "#{seed}#{Time.now.usec}".to_i % 2147483647 # Max paybox value for the question number
192
+
193
+ "0000000000#{randkey}"[-10..-1]
194
+ end
195
+
196
+ def expdate(credit_card)
197
+ year = sprintf("%.4i", credit_card.year)
198
+ month = sprintf("%.2i", credit_card.month)
199
+
200
+ "#{month}#{year[-2..-1]}"
201
+ end
202
+
203
+ end
204
+ end
205
+ end