activemerchant 1.22.0 → 1.23.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,275 @@
1
+ require 'rubygems'
2
+ require 'LitleOnline'
3
+
4
+ module ActiveMerchant #:nodoc:
5
+ module Billing #:nodoc:
6
+ class LitleGateway < Gateway
7
+ # Specific to Litle options:
8
+ # * <tt>:merchant_id</tt> - Merchant Id assigned by Litle
9
+ # * <tt>:user</tt> - Username assigned by Litle
10
+ # * <tt>:password</tt> - Password assigned by Litle
11
+ # * <tt>:version</tt> - The version of the api you are using (eg, '8.10')
12
+ # * <tt>:proxy_addr</tt> - Proxy address - nil if not needed
13
+ # * <tt>:proxy_port</tt> - Proxy port - nil if not needed
14
+ # * <tt>:url</tt> - URL assigned by Litle (for testing, use the sandbox)
15
+ #
16
+ # Standard Active Merchant options
17
+ # * <tt>:order_id</tt> - The order number
18
+ # * <tt>:ip</tt> - The IP address of the customer making the purchase
19
+ # * <tt>:customer</tt> - The name, customer number, or other information that identifies the customer
20
+ # * <tt>:invoice</tt> - The invoice number
21
+ # * <tt>:merchant</tt> - The name or description of the merchant offering the product
22
+ # * <tt>:description</tt> - A description of the transaction
23
+ # * <tt>:email</tt> - The email address of the customer
24
+ # * <tt>:currency</tt> - The currency of the transaction. Only important when you are using a currency that is not the default with a gateway that supports multiple currencies.
25
+ # * <tt>:billing_address</tt> - A hash containing the billing address of the customer.
26
+ # * <tt>:shipping_address</tt> - A hash containing the shipping address of the customer.
27
+ #
28
+ # The <tt>:billing_address</tt>, and <tt>:shipping_address</tt> hashes can have the following keys:
29
+ #
30
+ # * <tt>:name</tt> - The full name of the customer.
31
+ # * <tt>:company</tt> - The company name of the customer.
32
+ # * <tt>:address1</tt> - The primary street address of the customer.
33
+ # * <tt>:address2</tt> - Additional line of address information.
34
+ # * <tt>:city</tt> - The city of the customer.
35
+ # * <tt>:state</tt> - The state of the customer. The 2 digit code for US and Canadian addresses. The full name of the state or province for foreign addresses.
36
+ # * <tt>:country</tt> - The [ISO 3166-1-alpha-2 code](http://www.iso.org/iso/country_codes/iso_3166_code_lists/english_country_names_and_code_elements.htm) for the customer.
37
+ # * <tt>:zip</tt> - The zip or postal code of the customer.
38
+ # * <tt>:phone</tt> - The phone number of the customer.
39
+
40
+ TEST_URL = 'https://www.testlitle.com/sandbox/communicator/online'
41
+ LIVE_URL = 'https://payments.litle.com/vap/communicator/online'
42
+
43
+ # The countries the gateway supports merchants from as 2 digit ISO country codes
44
+ self.supported_countries = ['US']
45
+
46
+ # The card types supported by the payment gateway
47
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :jcb]
48
+
49
+ # The homepage URL of the gateway
50
+ self.homepage_url = 'http://www.litle.com/'
51
+
52
+ # The name of the gateway
53
+ self.display_name = 'Litle & Co.'
54
+
55
+ self.default_currency = 'USD'
56
+
57
+ def initialize(options = {})
58
+ @litle = LitleOnline::LitleOnlineRequest.new
59
+ requires!(options, :merchant_id, :user, :password, :version, :url)
60
+ @options = options
61
+ end
62
+
63
+ def authorize(money, creditcard, options = {})
64
+ to_pass = create_credit_card_hash(money, creditcard, options)
65
+ build_response(:authorization, @litle.authorization(to_pass))
66
+ end
67
+
68
+ def purchase(money, creditcard, options = {})
69
+ to_pass = create_credit_card_hash(money, creditcard, options)
70
+ build_response(:sale, @litle.sale(to_pass))
71
+ end
72
+
73
+ def capture(money, authorization, options = {})
74
+ to_pass = create_capture_hash(money, authorization, options)
75
+ build_response(:capture, @litle.capture(to_pass))
76
+ end
77
+
78
+ def void(identification, options = {})
79
+ to_pass = create_void_hash(identification, options)
80
+ build_response(:void, @litle.void(to_pass))
81
+ end
82
+
83
+ def credit(money, identification, options = {})
84
+ to_pass = create_credit_hash(money, identification, options)
85
+ build_response(:credit, @litle.credit(to_pass))
86
+ end
87
+
88
+ def store(creditcard, options = {})
89
+ to_pass = create_token_hash(creditcard, options)
90
+ build_response(:registerToken, @litle.register_token_request(to_pass), %w(801 802))
91
+ end
92
+
93
+ private
94
+
95
+ CARD_TYPE = {
96
+ 'visa' => 'VI',
97
+ 'master' => 'MC',
98
+ 'american_express' => 'AX',
99
+ 'discover' => 'DI',
100
+ 'jcb' => 'DI',
101
+ 'diners_club' => 'DI'
102
+ }
103
+
104
+ AVS_RESPONSE_CODE = {
105
+ '00' => 'Y',
106
+ '01' => 'X',
107
+ '02' => 'D',
108
+ '10' => 'Z',
109
+ '11' => 'W',
110
+ '12' => 'A',
111
+ '13' => 'A',
112
+ '14' => 'P',
113
+ '20' => 'N',
114
+ '30' => 'S',
115
+ '31' => 'R',
116
+ '32' => 'U',
117
+ '33' => 'R',
118
+ '34' => 'I',
119
+ '40' => 'E'
120
+ }
121
+
122
+ def build_response(kind, litle_response, valid_responses=%w(000))
123
+ if litle_response.response == "0"
124
+ detail = litle_response.send("#{kind}Response")
125
+ Response.new(
126
+ (valid_responses.include?(detail.response)),
127
+ detail.message,
128
+ {:litleOnlineResponse => litle_response},
129
+ :authorization => detail.litleTxnId,
130
+ :avs_result => {:code => fraud_result(detail)['avs']},
131
+ :cvv_result => fraud_result(detail)['cvv']
132
+ )
133
+ else
134
+ Response.new(false, litle_response.message, :litleOnlineResponse => litle_response)
135
+ end
136
+ end
137
+
138
+ def create_credit_card_hash(money, creditcard, options)
139
+ cc_type = CARD_TYPE[creditcard.type]
140
+
141
+ exp_date_yr = creditcard.year.to_s()[2..3]
142
+
143
+ if( creditcard.month.to_s().length == 1 )
144
+ exp_date_mo = '0' + creditcard.month.to_s()
145
+ else
146
+ exp_date_mo = creditcard.month.to_s()
147
+ end
148
+
149
+ exp_date = exp_date_mo + exp_date_yr
150
+
151
+ card_info = {
152
+ 'type' => cc_type,
153
+ 'number' => creditcard.number,
154
+ 'expDate' => exp_date,
155
+ 'cardValidationNum' => creditcard.verification_value
156
+ }
157
+
158
+ hash = create_hash(money, options)
159
+ hash['card'] = card_info
160
+ hash
161
+ end
162
+
163
+ def create_capture_hash(money, authorization, options)
164
+ hash = create_hash(money, options)
165
+ hash['litleTxnId'] = authorization
166
+ hash
167
+ end
168
+
169
+ def create_credit_hash(money, identification, options)
170
+ hash = create_hash(money, options)
171
+ hash['litleTxnId'] = identification
172
+ hash['orderSource'] = nil
173
+ hash['orderId'] = nil
174
+ hash
175
+ end
176
+
177
+ def create_token_hash(creditcard, options)
178
+ hash = create_hash(0, options)
179
+ hash['accountNumber'] = creditcard.number
180
+ hash
181
+ end
182
+
183
+ def create_void_hash(identification, options)
184
+ hash = create_hash(nil, options)
185
+ hash['litleTxnId'] = identification
186
+ hash
187
+ end
188
+
189
+ def create_hash(money, options)
190
+ fraud_check_type = {}
191
+ if options[:ip]
192
+ fraud_check_type['customerIpAddress'] = options[:ip]
193
+ end
194
+
195
+ enhanced_data = {}
196
+ if options[:invoice]
197
+ enhanced_data['invoiceReferenceNumber'] = options[:invoice]
198
+ end
199
+
200
+ if options[:description]
201
+ enhanced_data['customerReference'] = options[:description]
202
+ end
203
+
204
+ if options[:billing_address]
205
+ bill_to_address = {
206
+ 'name' => options[:billing_address][:name],
207
+ 'companyName' => options[:billing_address][:company],
208
+ 'addressLine1' => options[:billing_address][:address1],
209
+ 'addressLine2' => options[:billing_address][:address2],
210
+ 'city' => options[:billing_address][:city],
211
+ 'state' => options[:billing_address][:state],
212
+ 'zip' => options[:billing_address][:zip],
213
+ 'country' => options[:billing_address][:country],
214
+ 'email' => options[:email],
215
+ 'phone' => options[:billing_address][:phone]
216
+ }
217
+ end
218
+ if options[:shipping_address]
219
+ ship_to_address = {
220
+ 'name' => options[:shipping_address][:name],
221
+ 'companyName' => options[:shipping_address][:company],
222
+ 'addressLine1' => options[:shipping_address][:address1],
223
+ 'addressLine2' => options[:shipping_address][:address2],
224
+ 'city' => options[:shipping_address][:city],
225
+ 'state' => options[:shipping_address][:state],
226
+ 'zip' => options[:shipping_address][:zip],
227
+ 'country' => options[:shipping_address][:country],
228
+ 'email' => options[:email],
229
+ 'phone' => options[:shipping_address][:phone]
230
+ }
231
+ end
232
+
233
+ hash = {
234
+ 'billToAddress' => bill_to_address,
235
+ 'shipToAddress' => ship_to_address,
236
+ 'orderId' => (options[:order_id] or @options[:order_id]),
237
+ 'customerId' => options[:customer],
238
+ 'reportGroup' => (options[:merchant] or @options[:merchant]),
239
+ 'merchantId' => (options[:merchant_id] or @options[:merchant_id]),
240
+ 'orderSource' => 'ecommerce',
241
+ 'enhancedData' => enhanced_data,
242
+ 'fraudCheckType' => fraud_check_type,
243
+ 'user' => (options[:user] or @options[:user]),
244
+ 'password' => (options[:password] or @options[:password]),
245
+ 'version' => (options[:version] or @options[:version]),
246
+ 'url' => (options[:url] or @options[:url]),
247
+ 'proxy_addr' => (options[:proxy_addr] or @options[:proxy_addr]),
248
+ 'proxy_port' => (options[:proxy_port] or @options[:proxy_port]),
249
+ 'id' => (options[:id] or options[:order_id] or @options[:order_id])
250
+ }
251
+
252
+ if( !money.nil? && money.to_s.length > 0 )
253
+ hash.merge!({'amount' => money})
254
+ end
255
+ hash
256
+ end
257
+
258
+ def fraud_result(authorization_response)
259
+ if authorization_response.respond_to?('fraudResult')
260
+ fraud_result = authorization_response.fraudResult
261
+ if fraud_result.respond_to?('cardValidationResult')
262
+ cvv_to_pass = fraud_result.cardValidationResult
263
+ if(cvv_to_pass == "")
264
+ cvv_to_pass = "P"
265
+ end
266
+ end
267
+ if fraud_result.respond_to?('avsResult')
268
+ avs_to_pass = AVS_RESPONSE_CODE[fraud_result.avsResult]
269
+ end
270
+ end
271
+ {'cvv'=>cvv_to_pass, 'avs'=>avs_to_pass}
272
+ end
273
+ end
274
+ end
275
+ end
@@ -59,7 +59,7 @@ module ActiveMerchant #:nodoc:
59
59
  end
60
60
 
61
61
  def purchase(money, credit_card_or_stored_id, options = {})
62
- if credit_card_or_stored_id.is_a?(ActiveMerchant::Billing::CreditCard)
62
+ if credit_card_or_stored_id.respond_to?(:number)
63
63
  #Credit card for instant payment
64
64
  commit :purchase, build_purchase_request(money, credit_card_or_stored_id, options)
65
65
  else
@@ -20,7 +20,7 @@ module ActiveMerchant #:nodoc:
20
20
 
21
21
  self.homepage_url = 'http://www.paymentexpress.com/'
22
22
  self.display_name = 'PaymentExpress'
23
-
23
+
24
24
  URL = 'https://sec.paymentexpress.com/pxpost.aspx'
25
25
 
26
26
  APPROVED = '1'
@@ -34,8 +34,13 @@ module ActiveMerchant #:nodoc:
34
34
  }
35
35
 
36
36
  # We require the DPS gateway username and password when the object is created.
37
+ #
38
+ # The PaymentExpress gateway also supports a :use_custom_payment_token boolean option.
39
+ # If set to true the gateway will use BillingId for the Token type. If set to false,
40
+ # then the token will be sent as the DPS specified "DpsBillingId". This is per the documentation at
41
+ # http://www.paymentexpress.com/technical_resources/ecommerce_nonhosted/pxpost.html#Tokenbilling
37
42
  def initialize(options = {})
38
- # A DPS username and password must exist
43
+ # A DPS username and password must exist
39
44
  requires!(options, :login, :password)
40
45
  # Make the options an instance variable
41
46
  @options = options
@@ -43,6 +48,12 @@ module ActiveMerchant #:nodoc:
43
48
  end
44
49
 
45
50
  # Funds are transferred immediately.
51
+ #
52
+ # `payment_source` can be a usual ActiveMerchant credit_card object, or can also
53
+ # be a string of the `DpsBillingId` or `BillingId` which can be gotten through the
54
+ # store method. If you are using a `BillingId` instead of `DpsBillingId` you must
55
+ # also set the instance method `#use_billing_id_for_token` to true, see the `#store`
56
+ # method for an example of how to do this.
46
57
  def purchase(money, payment_source, options = {})
47
58
  request = build_purchase_or_authorization_request(money, payment_source, options)
48
59
  commit(:purchase, request)
@@ -51,6 +62,8 @@ module ActiveMerchant #:nodoc:
51
62
  # NOTE: Perhaps in options we allow a transaction note to be inserted
52
63
  # Verifies that funds are available for the requested card and amount and reserves the specified amount.
53
64
  # See: http://www.paymentexpress.com/technical_resources/ecommerce_nonhosted/pxpost.html#Authcomplete
65
+ #
66
+ # `payment_source` can be a usual ActiveMerchant credit_card object or a token, see #purchased method
54
67
  def authorize(money, payment_source, options = {})
55
68
  request = build_purchase_or_authorization_request(money, payment_source, options)
56
69
  commit(:authorization, request)
@@ -76,18 +89,47 @@ module ActiveMerchant #:nodoc:
76
89
  refund(money, identification, options)
77
90
  end
78
91
 
79
- # token based billing
80
-
81
- # initiates a "Validate" transcation to store card data on payment express servers
82
- # returns a "token" that can be used to rebill this card
92
+ # Token Based Billing
93
+ #
94
+ # Instead of storing the credit card details locally, you can store them inside the
95
+ # Payment Express system and instead bill future transactions against a token.
96
+ #
97
+ # This token can either be specified by your code or autogenerated by the PaymentExpress
98
+ # system. The default is to let PaymentExpress generate the token for you and so use
99
+ # the `DpsBillingId`. If you do not pass in any option of the `billing_id`, then the store
100
+ # method will ask PaymentExpress to create a token for you. Additionally, if you are
101
+ # using the default `DpsBillingId`, you do not have to do anything extra in the
102
+ # initialization of your gateway object.
103
+ #
104
+ # To specify and use your own token, you need to do two things.
105
+ #
106
+ # Firstly, pass in a `:billing_id` as an option in the hash of this store method. No
107
+ # validation is done on this BillingId by PaymentExpress so you must ensure that it is unique.
108
+ #
109
+ # gateway.store(credit_card, {:billing_id => 'YourUniqueBillingId'})
110
+ #
111
+ # Secondly, you will need to pass in the option `{:use_custom_payment_token => true}` when
112
+ # initializing your gateway instance, like so:
113
+ #
114
+ # gateway = ActiveMerchant::Billing::PaymentExpressGateway.new(
115
+ # :login => 'USERNAME',
116
+ # :password => 'PASSWORD',
117
+ # :use_custom_payment_token => true
118
+ # )
119
+ #
83
120
  # see: http://www.paymentexpress.com/technical_resources/ecommerce_nonhosted/pxpost.html#Tokenbilling
84
- # PaymentExpress does not support unstoring a stored card.
121
+ #
122
+ # Note, once stored, PaymentExpress does not support unstoring a stored card.
85
123
  def store(credit_card, options = {})
86
124
  request = build_token_request(credit_card, options)
87
125
  commit(:validate, request)
88
126
  end
89
-
127
+
90
128
  private
129
+
130
+ def use_custom_payment_token?
131
+ @options[:use_custom_payment_token]
132
+ end
91
133
 
92
134
  def build_purchase_or_authorization_request(money, payment_source, options)
93
135
  result = new_transaction
@@ -137,6 +179,7 @@ module ActiveMerchant #:nodoc:
137
179
 
138
180
  if credit_card.verification_value?
139
181
  xml.add_element("Cvc2").text = credit_card.verification_value
182
+ xml.add_element("Cvc2Presence").text = "1"
140
183
  end
141
184
 
142
185
  if requires_start_date_or_issue_number?(credit_card)
@@ -144,9 +187,13 @@ module ActiveMerchant #:nodoc:
144
187
  xml.add_element("IssueNumber").text = credit_card.issue_number unless credit_card.issue_number.blank?
145
188
  end
146
189
  end
147
-
148
- def add_billing_token(xml, token)
149
- xml.add_element("DpsBillingId").text = token
190
+
191
+ def add_billing_token(xml, token)
192
+ if use_custom_payment_token?
193
+ xml.add_element("BillingId").text = token
194
+ else
195
+ xml.add_element("DpsBillingId").text = token
196
+ end
150
197
  end
151
198
 
152
199
  def add_token_request(xml, options)
@@ -232,4 +279,4 @@ module ActiveMerchant #:nodoc:
232
279
  end
233
280
  end
234
281
  end
235
- end
282
+ end
@@ -64,7 +64,7 @@ module ActiveMerchant #:nodoc:
64
64
  end
65
65
 
66
66
  def purchase(money, credit_card_or_stored_id, options = {})
67
- if credit_card_or_stored_id.is_a?(ActiveMerchant::Billing::CreditCard)
67
+ if credit_card_or_stored_id.respond_to?(:number)
68
68
  requires!(options, :order_id)
69
69
  commit :purchase, build_purchase_request(money, credit_card_or_stored_id, options)
70
70
  else
@@ -126,7 +126,8 @@ module ActiveMerchant #:nodoc:
126
126
  def build_reference_request(money, reference)
127
127
  xml = Builder::XmlMarkup.new
128
128
 
129
- transaction_id, order_id, preauth_id, original_amount = reference.split("*")
129
+ transaction_id, order_id, preauth_id, original_amount = reference.split('*')
130
+
130
131
  xml.tag! 'amount', (money ? amount(money) : original_amount)
131
132
  xml.tag! 'currency', options[:currency] || currency(money)
132
133
  xml.tag! 'txnID', transaction_id
@@ -7,7 +7,7 @@ module ActiveMerchant #:nodoc:
7
7
  autoload 'Notification', File.dirname(__FILE__) + '/two_checkout/notification'
8
8
 
9
9
  mattr_accessor :payment_routine
10
- self.payment_routine = :multi_page
10
+ self.payment_routine = :single_page
11
11
 
12
12
  def self.service_url
13
13
  case self.payment_routine
@@ -6,67 +6,84 @@ module ActiveMerchant #:nodoc:
6
6
  def initialize(order, account, options = {})
7
7
  super
8
8
  add_field('fixed', 'Y')
9
-
9
+
10
10
  if ActiveMerchant::Billing::Base.integration_mode == :test || options[:test]
11
11
  add_field('demo', 'Y')
12
- end
12
+ end
13
13
  end
14
-
14
+
15
15
  # The 2checkout vendor account number
16
16
  mapping :account, 'sid'
17
-
18
- # he total amount to be billed, in decimal form, without a currency symbol. (8 characters, decimal, 2 characters: Example: 99999999.99)
17
+
18
+ # The total amount to be billed, in decimal form, without a currency symbol. (8 characters, decimal, 2 characters: Example: 99999999.99)
19
19
  mapping :amount, 'total'
20
-
21
- # a unique order id from your program. (128 characters max)
20
+
21
+ # Pass your order id if you are using Third Part Cart Parameters. (128 characters max)
22
22
  mapping :order, 'cart_order_id'
23
23
 
24
+ # Pass your order id if you are using the Pass Through Products Parameters. (50 characters max)
25
+ mapping :invoice, 'merchant_order_id'
26
+
27
+ # Left here for backward compatibility, do not use. The line_item method will add automatically.
24
28
  mapping :mode, 'mode'
25
29
 
26
30
  mapping :customer, :email => 'email',
27
- :phone => 'phone'
31
+ :phone => 'phone'
28
32
 
29
33
  mapping :billing_address, :city => 'city',
30
- :address1 => 'street_address',
31
- :address2 => 'street_address2',
32
- :state => 'state',
33
- :zip => 'zip',
34
- :country => 'country'
35
-
34
+ :address1 => 'street_address',
35
+ :address2 => 'street_address2',
36
+ :state => 'state',
37
+ :zip => 'zip',
38
+ :country => 'country'
39
+
36
40
  mapping :shipping_address, :city => 'ship_city',
37
- :address1 => 'ship_street_address',
38
- :state => 'ship_state',
39
- :zip => 'ship_zip',
40
- :country => 'ship_country'
41
-
42
- mapping :invoice, 'merchant_order_id'
43
-
41
+ :address1 => 'ship_street_address',
42
+ :state => 'ship_state',
43
+ :zip => 'ship_zip',
44
+ :country => 'ship_country'
45
+
44
46
  # Does nothing, since we've disabled the Continue Shopping button by using the fixed = Y field
45
47
  mapping :return_url, 'return_url'
46
-
47
- #mapping :description, ''
48
- #mapping :tax, ''
49
- #mapping :shipping, ''
50
-
48
+
49
+ # Approved URL path
50
+ mapping :notification_url, 'x_receipt_link_url'
51
+
51
52
  def customer(params = {})
52
53
  add_field(mappings[:customer][:email], params[:email])
53
54
  add_field(mappings[:customer][:phone], params[:phone])
54
55
  add_field('card_holder_name', "#{params[:first_name]} #{params[:last_name]}")
55
56
  end
56
-
57
+
58
+ # Uses Pass Through Product Parameters to pass in lineitems.
59
+ # (must mark tanigble sales as shipped to settle the transaction)
57
60
  def line_item(params = {})
58
- max_existing_line_item_id = form_fields.keys.map do |key|
59
- match = key.to_s.match(/li_(\d+)_/)
60
- if match
61
- match[1]
62
- end
63
- end.reject(&:nil?).max.to_i
64
-
61
+ add_field('mode', '2CO')
62
+ (max_existing_line_item_id = form_fields.keys.map do |key|
63
+ i = key.to_s[/^li_(\d+)_/, 1]
64
+ (i && i.to_i)
65
+ end.compact.max || 0)
66
+
65
67
  line_item_id = max_existing_line_item_id + 1
66
68
  params.each do |key, value|
67
69
  add_field("li_#{line_item_id}_#{key}", value)
68
70
  end
69
71
  end
72
+
73
+ # Uses Third Party Cart parameter set to pass in lineitem details.
74
+ # (sales settle automatically)
75
+ def auto_settle(params = {})
76
+ add_field('id_type', '1')
77
+ (max_existing_line_item_id = form_fields.keys.map do |key|
78
+ i = key.to_s[/^c_prod_(\d+)/, 1]
79
+ (i && i.to_i)
80
+ end.compact.max || 0)
81
+
82
+ line_item_id = max_existing_line_item_id + 1
83
+ params.each do |key, value|
84
+ add_field("c_#{key}_#{line_item_id}", value)
85
+ end
86
+ end
70
87
  end
71
88
  end
72
89
  end