activemerchant 1.22.0 → 1.23.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.
@@ -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