activemerchant 1.29.1 → 1.30.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.
- data/CHANGELOG +48 -0
- data/CONTRIBUTORS +19 -0
- data/README.md +43 -41
- data/lib/active_merchant/billing/check.rb +15 -11
- data/lib/active_merchant/billing/credit_card.rb +5 -1
- data/lib/active_merchant/billing/credit_card_formatting.rb +8 -8
- data/lib/active_merchant/billing/gateway.rb +1 -1
- data/lib/active_merchant/billing/gateways/authorize_net.rb +9 -1
- data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +15 -4
- data/lib/active_merchant/billing/gateways/balanced.rb +9 -3
- data/lib/active_merchant/billing/gateways/banwire.rb +15 -1
- data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +6 -2
- data/lib/active_merchant/billing/gateways/beanstream.rb +26 -24
- data/lib/active_merchant/billing/gateways/braintree_blue.rb +5 -2
- data/lib/active_merchant/billing/gateways/cyber_source.rb +55 -22
- data/lib/active_merchant/billing/gateways/eway.rb +114 -171
- data/lib/active_merchant/billing/gateways/eway_managed.rb +52 -22
- data/lib/active_merchant/billing/gateways/firstdata_e4.rb +222 -0
- data/lib/active_merchant/billing/gateways/ideal_rabobank.rb +13 -2
- data/lib/active_merchant/billing/gateways/litle.rb +50 -19
- data/lib/active_merchant/billing/gateways/merchant_ware.rb +44 -9
- data/lib/active_merchant/billing/gateways/merchant_warrior.rb +190 -0
- data/lib/active_merchant/billing/gateways/moneris.rb +3 -5
- data/lib/active_merchant/billing/gateways/moneris_us.rb +1 -1
- data/lib/active_merchant/billing/gateways/nab_transact.rb +20 -3
- data/lib/active_merchant/billing/gateways/netbilling.rb +1 -0
- data/lib/active_merchant/billing/gateways/netpay.rb +223 -0
- data/lib/active_merchant/billing/gateways/optimal_payment.rb +18 -3
- data/lib/active_merchant/billing/gateways/orbital.rb +9 -5
- data/lib/active_merchant/billing/gateways/payment_express.rb +62 -1
- data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +1 -1
- data/lib/active_merchant/billing/gateways/paypal_express.rb +2 -0
- data/lib/active_merchant/billing/gateways/pin.rb +157 -0
- data/lib/active_merchant/billing/gateways/qbms.rb +3 -2
- data/lib/active_merchant/billing/gateways/quickpay.rb +66 -28
- data/lib/active_merchant/billing/gateways/sage_pay.rb +6 -0
- data/lib/active_merchant/billing/gateways/smart_ps.rb +1 -1
- data/lib/active_merchant/billing/gateways/spreedly_core.rb +235 -0
- data/lib/active_merchant/billing/gateways/stripe.rb +1 -0
- data/lib/active_merchant/billing/gateways/wirecard.rb +15 -9
- data/lib/active_merchant/billing/gateways/worldpay.rb +15 -4
- data/lib/active_merchant/billing/integrations/payflow_link/helper.rb +4 -1
- data/lib/active_merchant/billing/integrations/paypal/notification.rb +39 -31
- data/lib/active_merchant/billing/integrations/quickpay/helper.rb +13 -10
- data/lib/active_merchant/billing/integrations/quickpay/notification.rb +14 -14
- data/lib/active_merchant/billing/integrations/sage_pay_form/helper.rb +2 -2
- data/lib/active_merchant/version.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +109 -49
- metadata.gz.sig +0 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
module ActiveMerchant #:nodoc:
|
|
2
|
+
module Billing #:nodoc:
|
|
3
|
+
#
|
|
4
|
+
# NETPAY Gateway
|
|
5
|
+
#
|
|
6
|
+
# Support for NETPAY's HTTP Connector payment gateway in Mexico.
|
|
7
|
+
#
|
|
8
|
+
# The gateway sends requests as HTTP POST and receives the response details
|
|
9
|
+
# in the HTTP header, making the process really rather simple.
|
|
10
|
+
#
|
|
11
|
+
# Support for calls to the authorize and capture methods have been included
|
|
12
|
+
# as per the Netpay manuals, however, your millage may vary with these
|
|
13
|
+
# methods. At the time of writing (January 2013) they were not fully
|
|
14
|
+
# supported by the production or test gateways. This situation is
|
|
15
|
+
# expected to change within a few weeks/months.
|
|
16
|
+
#
|
|
17
|
+
# Purchases can be cancelled (`#void`) only within 24 hours of the
|
|
18
|
+
# transaction. After this, a refund should be performed instead.
|
|
19
|
+
#
|
|
20
|
+
# In addition to the regular ActiveMerchant transaction options, NETPAY
|
|
21
|
+
# also supports a `:mode` parameter. This allows testing to be peformed
|
|
22
|
+
# in production and force specific results.
|
|
23
|
+
#
|
|
24
|
+
# * 'P' - Production
|
|
25
|
+
# * 'A' - Approved
|
|
26
|
+
# * 'D' - Declined
|
|
27
|
+
# * 'R' - Random (Approved or Declined)
|
|
28
|
+
# * 'T' - Test
|
|
29
|
+
#
|
|
30
|
+
# For example:
|
|
31
|
+
#
|
|
32
|
+
# response = @gateway.purchase(1000, card, :mode => 'D')
|
|
33
|
+
# response.success # false
|
|
34
|
+
#
|
|
35
|
+
class NetpayGateway < Gateway
|
|
36
|
+
self.test_url = 'http://200.57.87.243:8855'
|
|
37
|
+
self.live_url = 'https://suite.netpay.com.mx/acquirerprd'
|
|
38
|
+
|
|
39
|
+
# The countries the gateway supports merchants from as 2 digit ISO country codes
|
|
40
|
+
self.supported_countries = ['MX']
|
|
41
|
+
|
|
42
|
+
self.default_currency = 'MXN'
|
|
43
|
+
|
|
44
|
+
# The card types supported by the payment gateway
|
|
45
|
+
self.supported_cardtypes = [:visa, :master, :american_express, :diners_club]
|
|
46
|
+
|
|
47
|
+
# The homepage URL of the gateway
|
|
48
|
+
self.homepage_url = 'http://www.netpay.com.mx'
|
|
49
|
+
|
|
50
|
+
# The name of the gateway
|
|
51
|
+
self.display_name = 'NETPAY Gateway'
|
|
52
|
+
|
|
53
|
+
CURRENCY_CODES = {
|
|
54
|
+
"MXN" => '484'
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# The header keys that we will provide in the response params hash
|
|
58
|
+
RESPONSE_KEYS = ['ResponseMsg', 'ResponseText', 'ResponseCode', 'TimeIn', 'TimeOut', 'AuthCode', 'OrderId', 'CardTypeName', 'MerchantId', 'IssuerAuthDate']
|
|
59
|
+
|
|
60
|
+
def initialize(options = {})
|
|
61
|
+
requires!(options, :store_id, :login, :password)
|
|
62
|
+
super
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Send an authorization request
|
|
66
|
+
def authorize(money, creditcard, options = {})
|
|
67
|
+
post = {}
|
|
68
|
+
add_invoice(post, options)
|
|
69
|
+
add_creditcard(post, creditcard)
|
|
70
|
+
add_customer_data(post, options)
|
|
71
|
+
add_amount(post, money, options)
|
|
72
|
+
|
|
73
|
+
commit('PreAuth', post, options)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Capture an authorization
|
|
77
|
+
def capture(money, authorization, options = {})
|
|
78
|
+
post = {}
|
|
79
|
+
add_order_id(post, order_id_from(authorization))
|
|
80
|
+
add_amount(post, money, options)
|
|
81
|
+
|
|
82
|
+
commit('PostAuth', post, options)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Cancel an auth/purchase within first 24 hours
|
|
86
|
+
def void(authorization, options = {})
|
|
87
|
+
post = {}
|
|
88
|
+
order_id, amount, currency = split_authorization(authorization)
|
|
89
|
+
add_order_id(post, order_id)
|
|
90
|
+
post['Total'] = (options[:amount] || amount)
|
|
91
|
+
post['CurrencyCode'] = currency
|
|
92
|
+
|
|
93
|
+
commit('Refund', post, options)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Make a purchase.
|
|
97
|
+
def purchase(money, creditcard, options = {})
|
|
98
|
+
post = {}
|
|
99
|
+
add_invoice(post, options)
|
|
100
|
+
add_creditcard(post, creditcard)
|
|
101
|
+
add_customer_data(post, options)
|
|
102
|
+
add_amount(post, money, options)
|
|
103
|
+
|
|
104
|
+
commit('Auth', post, options)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Perform a Credit transaction.
|
|
108
|
+
def refund(money, authorization, options = {})
|
|
109
|
+
post = {}
|
|
110
|
+
add_order_id(post, order_id_from(authorization))
|
|
111
|
+
add_amount(post, money, options)
|
|
112
|
+
|
|
113
|
+
#commit('Refund', post, options)
|
|
114
|
+
commit('Credit', post, options)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
private
|
|
118
|
+
|
|
119
|
+
def add_login_data(post)
|
|
120
|
+
post['StoreId'] = @options[:store_id]
|
|
121
|
+
post['UserName'] = @options[:login]
|
|
122
|
+
post['Password'] = @options[:password]
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def add_action(post, action, options)
|
|
126
|
+
post['ResourceName'] = action
|
|
127
|
+
post['ContentType'] = 'Transaction'
|
|
128
|
+
post['Mode'] = options[:mode] || 'P'
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def add_order_id(post, order_id)
|
|
132
|
+
post['OrderId'] = order_id
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def add_amount(post, money, options)
|
|
136
|
+
post['Total'] = amount(money)
|
|
137
|
+
post['CurrencyCode'] = currency_code(options[:currency] || currency(money))
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def add_customer_data(post, options)
|
|
141
|
+
post['IPAddress'] = options[:ip] unless options[:ip].blank?
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def add_invoice(post, options)
|
|
145
|
+
post['Comments'] = options[:description] if options[:description]
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def add_creditcard(post, creditcard)
|
|
149
|
+
post['CardNumber'] = creditcard.number
|
|
150
|
+
post['ExpDate'] = expdate(creditcard)
|
|
151
|
+
post['CustomerName'] = creditcard.name
|
|
152
|
+
post['CVV2'] = creditcard.verification_value unless creditcard.verification_value.nil?
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def build_authorization(request_params, response_params)
|
|
156
|
+
[response_params['OrderId'], request_params['Total'], request_params['CurrencyCode']].join('|')
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def split_authorization(authorization)
|
|
160
|
+
order_id, amount, currency = authorization.split("|")
|
|
161
|
+
[order_id, amount, currency]
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def order_id_from(authorization)
|
|
165
|
+
split_authorization(authorization).first
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def expdate(credit_card)
|
|
169
|
+
year = sprintf("%.4i", credit_card.year)
|
|
170
|
+
month = sprintf("%.2i", credit_card.month)
|
|
171
|
+
|
|
172
|
+
"#{month}/#{year[-2..-1]}"
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def url
|
|
176
|
+
test? ? test_url : live_url
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def parse(response, request_params)
|
|
180
|
+
response_params = params_from_response(response)
|
|
181
|
+
|
|
182
|
+
success = (response_params['ResponseCode'] == '00')
|
|
183
|
+
message = response_params['ResponseText'] || response_params['ResponseMsg']
|
|
184
|
+
options = @options.merge(:test => test?,
|
|
185
|
+
:authorization => build_authorization(request_params, response_params))
|
|
186
|
+
|
|
187
|
+
Response.new(success, message, response_params, options)
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def commit(action, parameters, options)
|
|
191
|
+
add_login_data(parameters)
|
|
192
|
+
add_action(parameters, action, options)
|
|
193
|
+
|
|
194
|
+
post = parameters.collect{|key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&")
|
|
195
|
+
parse(ssl_post(url, post), parameters)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Override the regular handle response so we can access the headers
|
|
199
|
+
def handle_response(response)
|
|
200
|
+
case response.code.to_i
|
|
201
|
+
when 200...300
|
|
202
|
+
response
|
|
203
|
+
else
|
|
204
|
+
raise ResponseError.new(response)
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Return a hash containing all the useful, or informative values from netpay
|
|
209
|
+
def params_from_response(response)
|
|
210
|
+
params = {}
|
|
211
|
+
RESPONSE_KEYS.each do |k|
|
|
212
|
+
params[k] = response[k] unless response[k].to_s.empty?
|
|
213
|
+
end
|
|
214
|
+
params
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def currency_code(currency)
|
|
218
|
+
return currency if currency =~ /^\d+$/
|
|
219
|
+
CURRENCY_CODES[currency]
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
end
|
|
@@ -90,7 +90,9 @@ module ActiveMerchant #:nodoc:
|
|
|
90
90
|
|
|
91
91
|
Response.new(successful?(response), message_from(response), hash_from_xml(response),
|
|
92
92
|
:test => test?,
|
|
93
|
-
:authorization => authorization_from(response)
|
|
93
|
+
:authorization => authorization_from(response),
|
|
94
|
+
:avs_result => { :code => avs_result_from(response) },
|
|
95
|
+
:cvv_result => cvv_result_from(response)
|
|
94
96
|
)
|
|
95
97
|
end
|
|
96
98
|
|
|
@@ -108,7 +110,15 @@ module ActiveMerchant #:nodoc:
|
|
|
108
110
|
end
|
|
109
111
|
|
|
110
112
|
def authorization_from(response)
|
|
111
|
-
|
|
113
|
+
get_text_from_document(response, '//confirmationNumber')
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def avs_result_from(response)
|
|
117
|
+
get_text_from_document(response, '//avsResponse')
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def cvv_result_from(response)
|
|
121
|
+
get_text_from_document(response, '//cvdResponse')
|
|
112
122
|
end
|
|
113
123
|
|
|
114
124
|
def hash_from_xml(response)
|
|
@@ -138,6 +148,11 @@ module ActiveMerchant #:nodoc:
|
|
|
138
148
|
xml.target!
|
|
139
149
|
end
|
|
140
150
|
|
|
151
|
+
def get_text_from_document(document, node)
|
|
152
|
+
node = REXML::XPath.first(document, node)
|
|
153
|
+
node && node.text
|
|
154
|
+
end
|
|
155
|
+
|
|
141
156
|
def cc_auth_request(money, opts)
|
|
142
157
|
xml_document('ccAuthRequestV1') do |xml|
|
|
143
158
|
build_merchant_account(xml, @options)
|
|
@@ -251,7 +266,7 @@ module ActiveMerchant #:nodoc:
|
|
|
251
266
|
xml.tag! 'country', CGI.escape(addr[:country] ) if addr[:country].present?
|
|
252
267
|
xml.tag! 'zip' , CGI.escape(addr[:zip] ) # this one's actually required
|
|
253
268
|
xml.tag! 'phone' , CGI.escape(addr[:phone] ) if addr[:phone].present?
|
|
254
|
-
|
|
269
|
+
xml.tag! 'email', CGI.escape(opts[:email]) if opts[:email]
|
|
255
270
|
end
|
|
256
271
|
end
|
|
257
272
|
|
|
@@ -125,9 +125,13 @@ module ActiveMerchant #:nodoc:
|
|
|
125
125
|
refund(money, authorization, options)
|
|
126
126
|
end
|
|
127
127
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
128
|
+
def void(authorization, options = {}, deprecated = {})
|
|
129
|
+
if(!options.kind_of?(Hash))
|
|
130
|
+
deprecated("Calling the void method with an amount parameter is deprecated and will be removed in a future version.")
|
|
131
|
+
return void(options, deprecated.merge(:amount => authorization))
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
order = build_void_request_xml(authorization, options)
|
|
131
135
|
commit(order, :void)
|
|
132
136
|
end
|
|
133
137
|
|
|
@@ -396,7 +400,7 @@ module ActiveMerchant #:nodoc:
|
|
|
396
400
|
xml.target!
|
|
397
401
|
end
|
|
398
402
|
|
|
399
|
-
def build_void_request_xml(
|
|
403
|
+
def build_void_request_xml(authorization, parameters = {})
|
|
400
404
|
tx_ref_num, order_id = split_authorization(authorization)
|
|
401
405
|
xml = xml_envelope
|
|
402
406
|
xml.tag! :Request do
|
|
@@ -404,7 +408,7 @@ module ActiveMerchant #:nodoc:
|
|
|
404
408
|
add_xml_credentials(xml)
|
|
405
409
|
xml.tag! :TxRefNum, tx_ref_num
|
|
406
410
|
xml.tag! :TxRefIdx, parameters[:transaction_index]
|
|
407
|
-
xml.tag! :AdjustedAmt, amount
|
|
411
|
+
xml.tag! :AdjustedAmt, parameters[:amount] # setting adjusted amount to nil will void entire amount
|
|
408
412
|
xml.tag! :OrderID, format_order_id(order_id || parameters[:order_id])
|
|
409
413
|
add_bin_merchant_and_terminal(xml, parameters)
|
|
410
414
|
end
|
|
@@ -140,6 +140,7 @@ module ActiveMerchant #:nodoc:
|
|
|
140
140
|
add_amount(result, money, options)
|
|
141
141
|
add_invoice(result, options)
|
|
142
142
|
add_address_verification_data(result, options)
|
|
143
|
+
add_optional_elements(result, options)
|
|
143
144
|
result
|
|
144
145
|
end
|
|
145
146
|
|
|
@@ -149,6 +150,7 @@ module ActiveMerchant #:nodoc:
|
|
|
149
150
|
add_amount(result, money, options)
|
|
150
151
|
add_invoice(result, options)
|
|
151
152
|
add_reference(result, identification)
|
|
153
|
+
add_optional_elements(result, options)
|
|
152
154
|
result
|
|
153
155
|
end
|
|
154
156
|
|
|
@@ -157,6 +159,7 @@ module ActiveMerchant #:nodoc:
|
|
|
157
159
|
add_credit_card(result, credit_card)
|
|
158
160
|
add_amount(result, 100, options) #need to make an auth request for $1
|
|
159
161
|
add_token_request(result, options)
|
|
162
|
+
add_optional_elements(result, options)
|
|
160
163
|
result
|
|
161
164
|
end
|
|
162
165
|
|
|
@@ -209,7 +212,7 @@ module ActiveMerchant #:nodoc:
|
|
|
209
212
|
|
|
210
213
|
def add_invoice(xml, options)
|
|
211
214
|
xml.add_element("TxnId").text = options[:order_id].to_s.slice(0, 16) unless options[:order_id].blank?
|
|
212
|
-
xml.add_element("MerchantReference").text = options[:description] unless options[:description].blank?
|
|
215
|
+
xml.add_element("MerchantReference").text = options[:description].to_s.slice(0, 50) unless options[:description].blank?
|
|
213
216
|
end
|
|
214
217
|
|
|
215
218
|
def add_address_verification_data(xml, options)
|
|
@@ -223,6 +226,52 @@ module ActiveMerchant #:nodoc:
|
|
|
223
226
|
xml.add_element("AvsPostCode").text = address[:zip]
|
|
224
227
|
end
|
|
225
228
|
|
|
229
|
+
# The options hash may contain optional data which will be passed
|
|
230
|
+
# through the the specialized optional fields at PaymentExpress
|
|
231
|
+
# as follows:
|
|
232
|
+
#
|
|
233
|
+
# {
|
|
234
|
+
# :client_type => :web, # Possible values are: :web, :ivr, :moto, :unattended, :internet, or :recurring
|
|
235
|
+
# :txn_data1 => "String up to 255 characters",
|
|
236
|
+
# :txn_data2 => "String up to 255 characters",
|
|
237
|
+
# :txn_data3 => "String up to 255 characters"
|
|
238
|
+
# }
|
|
239
|
+
#
|
|
240
|
+
# +:client_type+, while not documented for PxPost, will be sent as
|
|
241
|
+
# the +ClientType+ XML element as described in the documentation for
|
|
242
|
+
# the PaymentExpress WebService: http://www.paymentexpress.com/Technical_Resources/Ecommerce_NonHosted/WebService#clientType
|
|
243
|
+
# (PaymentExpress have confirmed that this value works the same in PxPost).
|
|
244
|
+
# The value sent for +:client_type+ will be normalized and sent
|
|
245
|
+
# as one of the explicit values allowed by PxPost:
|
|
246
|
+
#
|
|
247
|
+
# :web => "Web"
|
|
248
|
+
# :ivr => "IVR"
|
|
249
|
+
# :moto => "MOTO"
|
|
250
|
+
# :unattended => "Unattended"
|
|
251
|
+
# :internet => "Internet"
|
|
252
|
+
# :recurring => "Recurring"
|
|
253
|
+
#
|
|
254
|
+
# If you set the +:client_type+ to any value not listed above,
|
|
255
|
+
# the ClientType element WILL NOT BE INCLUDED at all in the
|
|
256
|
+
# POST data.
|
|
257
|
+
#
|
|
258
|
+
# +:txn_data1+, +:txn_data2+, and +:txn_data3+ will be sent as
|
|
259
|
+
# +TxnData1+, +TxnData2+, and +TxnData3+, respectively, and are
|
|
260
|
+
# free form fields of the merchant's choosing, as documented here:
|
|
261
|
+
# http://www.paymentexpress.com/technical_resources/ecommerce_nonhosted/pxpost.html#txndata
|
|
262
|
+
#
|
|
263
|
+
# These optional elements are added to all transaction types:
|
|
264
|
+
# +purchase+, +authorize+, +capture+, +refund+, +store+
|
|
265
|
+
def add_optional_elements(xml, options)
|
|
266
|
+
if client_type = normalized_client_type(options[:client_type])
|
|
267
|
+
xml.add_element("ClientType").text = client_type
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
xml.add_element("TxnData1").text = options[:txn_data1].to_s.slice(0,255) unless options[:txn_data1].blank?
|
|
271
|
+
xml.add_element("TxnData2").text = options[:txn_data2].to_s.slice(0,255) unless options[:txn_data2].blank?
|
|
272
|
+
xml.add_element("TxnData3").text = options[:txn_data3].to_s.slice(0,255) unless options[:txn_data3].blank?
|
|
273
|
+
end
|
|
274
|
+
|
|
226
275
|
def new_transaction
|
|
227
276
|
REXML::Document.new.add_element("Txn")
|
|
228
277
|
end
|
|
@@ -266,6 +315,18 @@ module ActiveMerchant #:nodoc:
|
|
|
266
315
|
def format_date(month, year)
|
|
267
316
|
"#{format(month, :two_digits)}#{format(year, :two_digits)}"
|
|
268
317
|
end
|
|
318
|
+
|
|
319
|
+
def normalized_client_type(client_type_from_options)
|
|
320
|
+
case client_type_from_options.to_s.downcase
|
|
321
|
+
when 'web' then "Web"
|
|
322
|
+
when 'ivr' then "IVR"
|
|
323
|
+
when 'moto' then "MOTO"
|
|
324
|
+
when 'unattended' then "Unattended"
|
|
325
|
+
when 'internet' then "Internet"
|
|
326
|
+
when 'recurring' then "Recurring"
|
|
327
|
+
else nil
|
|
328
|
+
end
|
|
329
|
+
end
|
|
269
330
|
end
|
|
270
331
|
|
|
271
332
|
class PaymentExpressResponse < Response
|
|
@@ -397,7 +397,7 @@ module ActiveMerchant #:nodoc:
|
|
|
397
397
|
transaction_search_optional_fields = %w{ Payer ReceiptID Receiver
|
|
398
398
|
TransactionID InvoiceID CardNumber
|
|
399
399
|
AuctionItemNumber TransactionClass
|
|
400
|
-
CurrencyCode Status }
|
|
400
|
+
CurrencyCode Status ProfileID }
|
|
401
401
|
build_request_wrapper('TransactionSearch') do |xml|
|
|
402
402
|
xml.tag! 'StartDate', date_to_iso(options[:start_date])
|
|
403
403
|
xml.tag! 'EndDate', date_to_iso(options[:end_date]) unless options[:end_date].blank?
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require File.dirname(__FILE__) + '/paypal/paypal_common_api'
|
|
2
2
|
require File.dirname(__FILE__) + '/paypal/paypal_express_response'
|
|
3
|
+
require File.dirname(__FILE__) + '/paypal/paypal_recurring_api'
|
|
3
4
|
require File.dirname(__FILE__) + '/paypal_express_common'
|
|
4
5
|
|
|
5
6
|
module ActiveMerchant #:nodoc:
|
|
@@ -7,6 +8,7 @@ module ActiveMerchant #:nodoc:
|
|
|
7
8
|
class PaypalExpressGateway < Gateway
|
|
8
9
|
include PaypalCommonAPI
|
|
9
10
|
include PaypalExpressCommon
|
|
11
|
+
include PaypalRecurringApi
|
|
10
12
|
|
|
11
13
|
NON_STANDARD_LOCALE_CODES = {
|
|
12
14
|
'DK' => 'da_DK',
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
module ActiveMerchant #:nodoc:
|
|
2
|
+
module Billing #:nodoc:
|
|
3
|
+
class PinGateway < Gateway
|
|
4
|
+
self.test_url = 'https://test-api.pin.net.au/1'
|
|
5
|
+
self.live_url = 'https://api.pin.net.au/1'
|
|
6
|
+
|
|
7
|
+
self.default_currency = 'AUD'
|
|
8
|
+
self.money_format = :cents
|
|
9
|
+
self.supported_countries = ['AU']
|
|
10
|
+
self.supported_cardtypes = [:visa, :master]
|
|
11
|
+
self.homepage_url = 'http://www.pin.net.au/'
|
|
12
|
+
self.display_name = 'Pin'
|
|
13
|
+
|
|
14
|
+
def initialize(options = {})
|
|
15
|
+
requires!(options, :api_key)
|
|
16
|
+
super
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Create a charge using a credit card, card token or customer token
|
|
20
|
+
#
|
|
21
|
+
# To charge a credit card: purchase([money], [creditcard hash], ...)
|
|
22
|
+
# To charge a customer: purchase([money], [token], ...)
|
|
23
|
+
def purchase(money, creditcard, options = {})
|
|
24
|
+
post = {}
|
|
25
|
+
|
|
26
|
+
add_amount(post, money, options)
|
|
27
|
+
add_customer_data(post, options)
|
|
28
|
+
add_invoice(post, options)
|
|
29
|
+
add_creditcard(post, creditcard)
|
|
30
|
+
add_address(post, creditcard, options)
|
|
31
|
+
|
|
32
|
+
commit('charges', post)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Create a customer and associated credit card. The token that is returned
|
|
36
|
+
# can be used instead of a credit card parameter in the purchase method
|
|
37
|
+
def store(creditcard, options = {})
|
|
38
|
+
post = {}
|
|
39
|
+
|
|
40
|
+
add_creditcard(post, creditcard)
|
|
41
|
+
add_customer_data(post, options)
|
|
42
|
+
add_address(post, creditcard, options)
|
|
43
|
+
commit('customers', post)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Refund a transaction, note that the money attribute is ignored at the
|
|
47
|
+
# moment as the API does not support partial refunds. The parameter is
|
|
48
|
+
# kept for compatibility reasons
|
|
49
|
+
def refund(money, token, options = {})
|
|
50
|
+
commit("charges/#{CGI.escape(token)}/refunds", :amount => amount(money))
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
def add_amount(post, money, options)
|
|
55
|
+
post[:amount] = amount(money)
|
|
56
|
+
post[:currency] = (options[:currency] || currency(money))
|
|
57
|
+
post[:currency] = post[:currency].upcase if post[:currency]
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def add_customer_data(post, options)
|
|
61
|
+
post[:email] = options[:email]
|
|
62
|
+
post[:ip_address] = options[:ip]
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def add_address(post, creditcard, options)
|
|
66
|
+
return if creditcard.kind_of?(String)
|
|
67
|
+
address = (options[:billing_address] || options[:address])
|
|
68
|
+
return unless address
|
|
69
|
+
|
|
70
|
+
post[:card] ||= {}
|
|
71
|
+
post[:card].merge!(
|
|
72
|
+
:address_line1 => address[:address1],
|
|
73
|
+
:address_city => address[:city],
|
|
74
|
+
:address_postcode => address[:zip],
|
|
75
|
+
:address_state => address[:state],
|
|
76
|
+
:address_country => address[:country]
|
|
77
|
+
)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def add_invoice(post, options)
|
|
81
|
+
post[:description] = options[:description]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def add_creditcard(post, creditcard)
|
|
85
|
+
if creditcard.respond_to?(:number)
|
|
86
|
+
post[:card] ||= {}
|
|
87
|
+
|
|
88
|
+
post[:card].merge!(
|
|
89
|
+
:number => creditcard.number,
|
|
90
|
+
:expiry_month => creditcard.month,
|
|
91
|
+
:expiry_year => creditcard.year,
|
|
92
|
+
:cvc => creditcard.verification_value,
|
|
93
|
+
:name => "#{creditcard.first_name} #{creditcard.last_name}"
|
|
94
|
+
)
|
|
95
|
+
elsif creditcard.kind_of?(String)
|
|
96
|
+
post[:customer_token] = creditcard
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def headers
|
|
101
|
+
{
|
|
102
|
+
"Content-Type" => "application/json",
|
|
103
|
+
"Authorization" => "Basic #{Base64.strict_encode64(options[:api_key] + ':').strip}"
|
|
104
|
+
}
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def commit(action, params)
|
|
108
|
+
url = "#{test? ? test_url : live_url}/#{action}"
|
|
109
|
+
|
|
110
|
+
begin
|
|
111
|
+
body = parse(ssl_post(url, post_data(params), headers))
|
|
112
|
+
rescue ResponseError => e
|
|
113
|
+
body = parse(e.response.body)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
if body["response"]
|
|
117
|
+
success_response(body)
|
|
118
|
+
elsif body["error"]
|
|
119
|
+
error_response(body)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def success_response(body)
|
|
124
|
+
response = body["response"]
|
|
125
|
+
Response.new(
|
|
126
|
+
true,
|
|
127
|
+
response['status_message'],
|
|
128
|
+
body,
|
|
129
|
+
:authorization => token(response),
|
|
130
|
+
:test => test?
|
|
131
|
+
)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def error_response(body)
|
|
135
|
+
Response.new(
|
|
136
|
+
false,
|
|
137
|
+
body['error_description'],
|
|
138
|
+
body,
|
|
139
|
+
:authorization => nil,
|
|
140
|
+
:test => test?
|
|
141
|
+
)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def token(response)
|
|
145
|
+
response['token']
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def parse(body)
|
|
149
|
+
JSON.parse(body)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def post_data(parameters = {})
|
|
153
|
+
parameters.to_json
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
@@ -126,6 +126,7 @@ module ActiveMerchant #:nodoc:
|
|
|
126
126
|
parameters[:trans_request_id] ||= SecureRandom.hex(10)
|
|
127
127
|
|
|
128
128
|
req = build_request(type, money, parameters)
|
|
129
|
+
|
|
129
130
|
data = ssl_post(url, req, "Content-Type" => "application/x-qbmsxml")
|
|
130
131
|
response = parse(type, data)
|
|
131
132
|
message = (response[:status_message] || '').strip
|
|
@@ -260,8 +261,8 @@ module ActiveMerchant #:nodoc:
|
|
|
260
261
|
|
|
261
262
|
def add_address(xml, parameters)
|
|
262
263
|
if address = parameters[:billing_address] || parameters[:address]
|
|
263
|
-
xml.tag!("CreditCardAddress", address[:address1][0...30])
|
|
264
|
-
xml.tag!("CreditCardPostalCode", address[:zip][0...9])
|
|
264
|
+
xml.tag!("CreditCardAddress", (address[:address1] || "")[0...30])
|
|
265
|
+
xml.tag!("CreditCardPostalCode", (address[:zip] || "")[0...9])
|
|
265
266
|
end
|
|
266
267
|
end
|
|
267
268
|
|