activemerchant 1.28.0 → 1.29.1
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 +41 -0
- data/CONTRIBUTORS +12 -0
- data/README.md +6 -0
- data/lib/active_merchant/billing/gateway.rb +2 -1
- data/lib/active_merchant/billing/gateways/authorize_net.rb +3 -2
- data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +77 -78
- data/lib/active_merchant/billing/gateways/balanced.rb +0 -1
- data/lib/active_merchant/billing/gateways/banwire.rb +1 -2
- data/lib/active_merchant/billing/gateways/barclays_epdq.rb +19 -20
- data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +35 -36
- data/lib/active_merchant/billing/gateways/blue_pay.rb +135 -140
- data/lib/active_merchant/billing/gateways/braintree_blue.rb +12 -4
- data/lib/active_merchant/billing/gateways/card_stream.rb +54 -59
- data/lib/active_merchant/billing/gateways/certo_direct.rb +0 -1
- data/lib/active_merchant/billing/gateways/cyber_source.rb +19 -14
- data/lib/active_merchant/billing/gateways/data_cash.rb +106 -112
- data/lib/active_merchant/billing/gateways/efsnet.rb +29 -34
- data/lib/active_merchant/billing/gateways/elavon.rb +7 -1
- data/lib/active_merchant/billing/gateways/epay.rb +0 -1
- data/lib/active_merchant/billing/gateways/eway.rb +88 -93
- data/lib/active_merchant/billing/gateways/eway_managed.rb +47 -51
- data/lib/active_merchant/billing/gateways/eway_rapid.rb +300 -0
- data/lib/active_merchant/billing/gateways/exact.rb +45 -54
- data/lib/active_merchant/billing/gateways/federated_canada.rb +3 -4
- data/lib/active_merchant/billing/gateways/first_pay.rb +37 -38
- data/lib/active_merchant/billing/gateways/garanti.rb +1 -2
- data/lib/active_merchant/billing/gateways/hdfc.rb +207 -0
- data/lib/active_merchant/billing/gateways/ideal/ideal_base.rb +5 -8
- data/lib/active_merchant/billing/gateways/inspire.rb +52 -52
- data/lib/active_merchant/billing/gateways/instapay.rb +10 -11
- data/lib/active_merchant/billing/gateways/iridium.rb +38 -39
- data/lib/active_merchant/billing/gateways/itransact.rb +7 -9
- data/lib/active_merchant/billing/gateways/jetpay.rb +45 -46
- data/lib/active_merchant/billing/gateways/linkpoint.rb +104 -108
- data/lib/active_merchant/billing/gateways/litle.rb +1 -5
- data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +153 -155
- data/lib/active_merchant/billing/gateways/merchant_ware.rb +49 -50
- data/lib/active_merchant/billing/gateways/mercury.rb +272 -0
- data/lib/active_merchant/billing/gateways/metrics_global.rb +9 -10
- data/lib/active_merchant/billing/gateways/migs.rb +5 -3
- data/lib/active_merchant/billing/gateways/modern_payments.rb +6 -7
- data/lib/active_merchant/billing/gateways/modern_payments_cim.rb +40 -41
- data/lib/active_merchant/billing/gateways/moneris.rb +46 -50
- data/lib/active_merchant/billing/gateways/moneris_us.rb +52 -55
- data/lib/active_merchant/billing/gateways/nab_transact.rb +0 -5
- data/lib/active_merchant/billing/gateways/net_registry.rb +20 -21
- data/lib/active_merchant/billing/gateways/netaxept.rb +30 -36
- data/lib/active_merchant/billing/gateways/netbilling.rb +2 -2
- data/lib/active_merchant/billing/gateways/ogone.rb +0 -5
- data/lib/active_merchant/billing/gateways/optimal_payment.rb +1 -6
- data/lib/active_merchant/billing/gateways/orbital/avs_result.rb +93 -0
- data/lib/active_merchant/billing/gateways/orbital.rb +25 -21
- data/lib/active_merchant/billing/gateways/pay_gate_xml.rb +1 -6
- data/lib/active_merchant/billing/gateways/pay_junction.rb +62 -63
- data/lib/active_merchant/billing/gateways/pay_secure.rb +29 -30
- data/lib/active_merchant/billing/gateways/paybox_direct.rb +0 -5
- data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +33 -38
- data/lib/active_merchant/billing/gateways/payment_express.rb +48 -51
- data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +7 -11
- data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +7 -0
- data/lib/active_merchant/billing/gateways/paypal/paypal_recurring_api.rb +3 -0
- data/lib/active_merchant/billing/gateways/paystation.rb +62 -64
- data/lib/active_merchant/billing/gateways/payway.rb +2 -9
- data/lib/active_merchant/billing/gateways/plugnpay.rb +0 -1
- data/lib/active_merchant/billing/gateways/psigate.rb +102 -94
- data/lib/active_merchant/billing/gateways/psl_card.rb +66 -67
- data/lib/active_merchant/billing/gateways/qbms.rb +0 -6
- data/lib/active_merchant/billing/gateways/quantum.rb +2 -8
- data/lib/active_merchant/billing/gateways/quickpay.rb +2 -3
- data/lib/active_merchant/billing/gateways/realex.rb +6 -16
- data/lib/active_merchant/billing/gateways/redsys.rb +394 -0
- data/lib/active_merchant/billing/gateways/sage/sage_core.rb +25 -26
- data/lib/active_merchant/billing/gateways/sage.rb +15 -16
- data/lib/active_merchant/billing/gateways/sage_pay.rb +51 -56
- data/lib/active_merchant/billing/gateways/sallie_mae.rb +1 -2
- data/lib/active_merchant/billing/gateways/samurai.rb +1 -4
- data/lib/active_merchant/billing/gateways/secure_net.rb +0 -1
- data/lib/active_merchant/billing/gateways/secure_pay.rb +5 -8
- data/lib/active_merchant/billing/gateways/secure_pay_au.rb +0 -5
- data/lib/active_merchant/billing/gateways/secure_pay_tech.rb +17 -18
- data/lib/active_merchant/billing/gateways/skip_jack.rb +29 -34
- data/lib/active_merchant/billing/gateways/smart_ps.rb +55 -56
- data/lib/active_merchant/billing/gateways/stripe.rb +8 -3
- data/lib/active_merchant/billing/gateways/trans_first.rb +28 -29
- data/lib/active_merchant/billing/gateways/trust_commerce.rb +85 -87
- data/lib/active_merchant/billing/gateways/usa_epay_advanced.rb +27 -28
- data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +0 -5
- data/lib/active_merchant/billing/gateways/verifi.rb +86 -87
- data/lib/active_merchant/billing/gateways/viaklix.rb +42 -47
- data/lib/active_merchant/billing/gateways/vindicia.rb +30 -28
- data/lib/active_merchant/billing/gateways/webpay.rb +45 -0
- data/lib/active_merchant/billing/gateways/wirecard.rb +0 -6
- data/lib/active_merchant/billing/gateways/worldpay.rb +4 -9
- data/lib/active_merchant/billing/gateways.rb +6 -7
- data/lib/active_merchant/billing/integrations/a1agregator/helper.rb +31 -0
- data/lib/active_merchant/billing/integrations/a1agregator/notification.rb +186 -0
- data/lib/active_merchant/billing/integrations/a1agregator/status.rb +38 -0
- data/lib/active_merchant/billing/integrations/a1agregator.rb +26 -0
- data/lib/active_merchant/billing/integrations/liqpay/helper.rb +43 -0
- data/lib/active_merchant/billing/integrations/liqpay/notification.rb +89 -0
- data/lib/active_merchant/billing/integrations/liqpay/return.rb +83 -0
- data/lib/active_merchant/billing/integrations/liqpay.rb +30 -0
- data/lib/active_merchant/billing/integrations/moneybookers/helper.rb +17 -1
- data/lib/active_merchant/billing/integrations/notification.rb +4 -0
- data/lib/active_merchant/billing/integrations/pay_fast/common.rb +42 -0
- data/lib/active_merchant/billing/integrations/pay_fast/helper.rb +50 -0
- data/lib/active_merchant/billing/integrations/pay_fast/notification.rb +134 -0
- data/lib/active_merchant/billing/integrations/pay_fast/return.rb +10 -0
- data/lib/active_merchant/billing/integrations/pay_fast.rb +70 -0
- data/lib/active_merchant/billing/integrations/paypal/notification.rb +64 -0
- data/lib/active_merchant/billing/integrations/sage_pay_form/helper.rb +10 -7
- data/lib/active_merchant/billing/integrations/webmoney/notification.rb +12 -0
- data/lib/active_merchant/billing/response.rb +17 -4
- data/lib/active_merchant/version.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +46 -27
- metadata.gz.sig +0 -0
|
@@ -37,15 +37,9 @@ module ActiveMerchant #:nodoc:
|
|
|
37
37
|
#
|
|
38
38
|
def initialize(options = {})
|
|
39
39
|
requires!(options, :login, :password)
|
|
40
|
-
@options = options
|
|
41
40
|
super
|
|
42
41
|
end
|
|
43
42
|
|
|
44
|
-
# Should run against the test servers or not?
|
|
45
|
-
def test?
|
|
46
|
-
@options[:test] || Base.gateway_mode == :test
|
|
47
|
-
end
|
|
48
|
-
|
|
49
43
|
# Request an authorization for an amount from CyberSource
|
|
50
44
|
#
|
|
51
45
|
def authorize(money, creditcard, options = {})
|
|
@@ -268,11 +262,11 @@ module ActiveMerchant #:nodoc:
|
|
|
268
262
|
end
|
|
269
263
|
return reply
|
|
270
264
|
end
|
|
271
|
-
|
|
265
|
+
|
|
272
266
|
def authorization_for(reply)
|
|
273
267
|
"#{reply[:TransactionID]};#{reply[:CreditCardNumber]}"
|
|
274
268
|
end
|
|
275
|
-
|
|
269
|
+
|
|
276
270
|
def authorization_parts_from(authorization)
|
|
277
271
|
authorization.split(/;/)
|
|
278
272
|
end
|
|
@@ -93,7 +93,6 @@ module ActiveMerchant #:nodoc:
|
|
|
93
93
|
def initialize(options = {})
|
|
94
94
|
requires!(options, :login, :password)
|
|
95
95
|
@protocol = options.delete(:version) || 3 # default to protocol version 3
|
|
96
|
-
@options = options
|
|
97
96
|
super
|
|
98
97
|
end
|
|
99
98
|
|
|
@@ -215,10 +214,10 @@ module ActiveMerchant #:nodoc:
|
|
|
215
214
|
end
|
|
216
215
|
|
|
217
216
|
def add_testmode(post)
|
|
218
|
-
return if post[:transaction].present?
|
|
217
|
+
return if post[:transaction].present?
|
|
219
218
|
post[:testmode] = test? ? '1' : '0'
|
|
220
219
|
end
|
|
221
|
-
|
|
220
|
+
|
|
222
221
|
def add_fraud_parameters(post, options)
|
|
223
222
|
if @protocol == 4
|
|
224
223
|
post[:fraud_remote_addr] = options[:fraud_remote_addr] if options[:fraud_remote_addr]
|
|
@@ -20,7 +20,7 @@ module ActiveMerchant
|
|
|
20
20
|
# same order id
|
|
21
21
|
class RealexGateway < Gateway
|
|
22
22
|
self.live_url = self.test_url = 'https://epage.payandshop.com/epage-remote.cgi'
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
CARD_MAPPING = {
|
|
25
25
|
'master' => 'MC',
|
|
26
26
|
'visa' => 'VISA',
|
|
@@ -45,7 +45,6 @@ module ActiveMerchant
|
|
|
45
45
|
def initialize(options = {})
|
|
46
46
|
requires!(options, :login, :password)
|
|
47
47
|
options[:refund_hash] = Digest::SHA1.hexdigest(options[:rebate_secret]) if options.has_key?(:rebate_secret)
|
|
48
|
-
@options = options
|
|
49
48
|
super
|
|
50
49
|
end
|
|
51
50
|
|
|
@@ -81,10 +80,10 @@ module ActiveMerchant
|
|
|
81
80
|
def void(authorization, options = {})
|
|
82
81
|
request = build_void_request(authorization, options)
|
|
83
82
|
commit(request)
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
private
|
|
87
|
-
def commit(request)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
private
|
|
86
|
+
def commit(request)
|
|
88
87
|
response = parse(ssl_post(self.live_url, request))
|
|
89
88
|
|
|
90
89
|
Response.new(response[:result] == "00", message_from(response), response,
|
|
@@ -188,7 +187,7 @@ module ActiveMerchant
|
|
|
188
187
|
|
|
189
188
|
if billing_address
|
|
190
189
|
xml.tag! 'address', 'type' => 'billing' do
|
|
191
|
-
xml.tag! 'code',
|
|
190
|
+
xml.tag! 'code', format_shipping_zip_code(billing_address[:zip])
|
|
192
191
|
xml.tag! 'country', billing_address[:country]
|
|
193
192
|
end
|
|
194
193
|
end
|
|
@@ -241,19 +240,10 @@ module ActiveMerchant
|
|
|
241
240
|
end
|
|
242
241
|
end
|
|
243
242
|
|
|
244
|
-
def avs_input_code(address)
|
|
245
|
-
address.values_at(:zip, :address1).map{ |v| extract_digits(v) }.join('|')
|
|
246
|
-
end
|
|
247
|
-
|
|
248
243
|
def format_shipping_zip_code(zip)
|
|
249
244
|
zip.to_s.gsub(/\W/, '')
|
|
250
245
|
end
|
|
251
246
|
|
|
252
|
-
def extract_digits(string)
|
|
253
|
-
return "" if string.nil?
|
|
254
|
-
string.gsub(/[\D]/,'')
|
|
255
|
-
end
|
|
256
|
-
|
|
257
247
|
def new_timestamp
|
|
258
248
|
Time.now.strftime('%Y%m%d%H%M%S')
|
|
259
249
|
end
|
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
require 'nokogiri'
|
|
3
|
+
|
|
4
|
+
module ActiveMerchant #:nodoc:
|
|
5
|
+
module Billing #:nodoc:
|
|
6
|
+
# = Redsys Merchant Gateway
|
|
7
|
+
#
|
|
8
|
+
# Gateway support for the Spanish "Redsys" payment gateway system. This is
|
|
9
|
+
# used by many banks in Spain and is particularly well supported by
|
|
10
|
+
# Catalunya Caixa's ecommerce department.
|
|
11
|
+
#
|
|
12
|
+
# Standard ActiveMerchant methods are supported, with one notable exception:
|
|
13
|
+
# :order_id must be provided and must conform to a very specific format.
|
|
14
|
+
#
|
|
15
|
+
# == Example use:
|
|
16
|
+
#
|
|
17
|
+
# gateway = ActiveMerchant::Billing::RedsysGateway.new(
|
|
18
|
+
# :login => "091358382",
|
|
19
|
+
# :secret_key => "qwertyasdf0123456789"
|
|
20
|
+
# )
|
|
21
|
+
#
|
|
22
|
+
# # Run a purchase for 10 euros
|
|
23
|
+
# response = gateway.purchase(1000, creditcard, :order_id => "123456")
|
|
24
|
+
# puts reponse.success? # => true
|
|
25
|
+
#
|
|
26
|
+
# # Partially refund the purchase
|
|
27
|
+
# response = gateway.refund(500, response.authorization)
|
|
28
|
+
#
|
|
29
|
+
# Redsys requires an order_id be provided with each transaction of a
|
|
30
|
+
# specific format. The rules are as follows:
|
|
31
|
+
#
|
|
32
|
+
# * Minimum length: 4
|
|
33
|
+
# * Maximum length: 12
|
|
34
|
+
# * First 4 digits must be numerical
|
|
35
|
+
# * Remaining 8 digits may be alphanumeric
|
|
36
|
+
#
|
|
37
|
+
# Much of the code for this library is based on the active_merchant_sermepa
|
|
38
|
+
# integration gateway which uses essentially the same API but with the
|
|
39
|
+
# banks own payment screen.
|
|
40
|
+
#
|
|
41
|
+
# Written by Samuel Lown for Cabify. For implementation questions, or
|
|
42
|
+
# test access details please get in touch: sam@cabify.com.
|
|
43
|
+
class RedsysGateway < Gateway
|
|
44
|
+
self.live_url = "https://sis.sermepa.es/sis/operaciones"
|
|
45
|
+
self.test_url = "https://sis-t.sermepa.es:25443/sis/operaciones"
|
|
46
|
+
|
|
47
|
+
# Sensible region specific defaults.
|
|
48
|
+
self.supported_countries = ['ES']
|
|
49
|
+
self.default_currency = 'EUR'
|
|
50
|
+
self.money_format = :cents
|
|
51
|
+
|
|
52
|
+
# Not all card types may be actived by the bank!
|
|
53
|
+
self.supported_cardtypes = [:visa, :master, :american_express, :jcb, :diners_club]
|
|
54
|
+
|
|
55
|
+
# Homepage URL of the gateway for reference
|
|
56
|
+
self.homepage_url = "http://www.redsys.es/"
|
|
57
|
+
|
|
58
|
+
# What to call this gateway
|
|
59
|
+
self.display_name = "Redsys"
|
|
60
|
+
|
|
61
|
+
CURRENCY_CODES = {
|
|
62
|
+
"ARS" => '032',
|
|
63
|
+
"AUD" => '036',
|
|
64
|
+
"BRL" => '986',
|
|
65
|
+
"BOB" => '068',
|
|
66
|
+
"CAD" => '124',
|
|
67
|
+
"CHF" => '756',
|
|
68
|
+
"CLP" => '152',
|
|
69
|
+
"COP" => '170',
|
|
70
|
+
"EUR" => '978',
|
|
71
|
+
"GBP" => '826',
|
|
72
|
+
"GTQ" => '320',
|
|
73
|
+
"JPY" => '392',
|
|
74
|
+
"MXN" => '484',
|
|
75
|
+
"NZD" => '554',
|
|
76
|
+
"PEN" => '604',
|
|
77
|
+
"RUB" => '643',
|
|
78
|
+
"USD" => '840',
|
|
79
|
+
"UYU" => '858'
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
# The set of supported transactions for this gateway.
|
|
83
|
+
# More operations are supported by the gateway itself, but
|
|
84
|
+
# are not supported in this library.
|
|
85
|
+
SUPPORTED_TRANSACTIONS = {
|
|
86
|
+
:purchase => 'A',
|
|
87
|
+
:authorize => '1',
|
|
88
|
+
:capture => '2',
|
|
89
|
+
:refund => '3',
|
|
90
|
+
:cancel => '9'
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
# These are the text meanings sent back by the acquirer when
|
|
94
|
+
# a card has been rejected. Syntax or general request errors
|
|
95
|
+
# are not covered here.
|
|
96
|
+
RESPONSE_TEXTS = {
|
|
97
|
+
# Accepted Codes
|
|
98
|
+
0 => "Transaction Approved",
|
|
99
|
+
400 => "Cancellation Accepted",
|
|
100
|
+
481 => "Cancellation Accepted",
|
|
101
|
+
500 => "Reconciliation Accepted",
|
|
102
|
+
900 => "Refund / Confirmation approved",
|
|
103
|
+
|
|
104
|
+
# Declined error codes
|
|
105
|
+
101 => "Card expired",
|
|
106
|
+
102 => "Card blocked temporarily or under susciption of fraud",
|
|
107
|
+
104 => "Transaction not permitted",
|
|
108
|
+
107 => "Contact the card issuer",
|
|
109
|
+
109 => "Invalid identification by merchant or POS terminal",
|
|
110
|
+
110 => "Invalid amount",
|
|
111
|
+
114 => "Card cannot be used to the requested transaction",
|
|
112
|
+
116 => "Insufficient credit",
|
|
113
|
+
118 => "Non-registered card",
|
|
114
|
+
125 => "Card not effective",
|
|
115
|
+
129 => "CVV2/CVC2 Error",
|
|
116
|
+
167 => "Contact the card issuer: suspected fraud",
|
|
117
|
+
180 => "Card out of service",
|
|
118
|
+
181 => "Card with credit or debit restrictions",
|
|
119
|
+
182 => "Card with credit or debit restrictions",
|
|
120
|
+
184 => "Authentication error",
|
|
121
|
+
190 => "Refusal with no specific reason",
|
|
122
|
+
191 => "Expiry date incorrect",
|
|
123
|
+
|
|
124
|
+
# Declined, and suspected of fraud
|
|
125
|
+
201 => "Card expired",
|
|
126
|
+
202 => "Card blocked temporarily or under suscipition of fraud",
|
|
127
|
+
204 => "Transaction not permitted",
|
|
128
|
+
207 => "Contact the card issuer",
|
|
129
|
+
208 => "Lost or stolen card",
|
|
130
|
+
209 => "Lost or stolen card",
|
|
131
|
+
280 => "CVV2/CVC2 Error",
|
|
132
|
+
290 => "Declined with no specific reason",
|
|
133
|
+
|
|
134
|
+
# More general codes for specific types of transaction
|
|
135
|
+
480 => "Original transaction not located, or time-out exceeded",
|
|
136
|
+
501 => "Original transaction not located, or time-out exceeded",
|
|
137
|
+
502 => "Original transaction not located, or time-out exceeded",
|
|
138
|
+
503 => "Original transaction not located, or time-out exceeded",
|
|
139
|
+
|
|
140
|
+
# Declined transactions by the bank
|
|
141
|
+
904 => "Merchant not registered at FUC",
|
|
142
|
+
909 => "System error",
|
|
143
|
+
912 => "Issuer not available",
|
|
144
|
+
913 => "Duplicate transmission",
|
|
145
|
+
916 => "Amount too low",
|
|
146
|
+
928 => "Time-out exceeded",
|
|
147
|
+
940 => "Transaction cancelled previously",
|
|
148
|
+
941 => "Authorization operation already cancelled",
|
|
149
|
+
942 => "Original authorization declined",
|
|
150
|
+
943 => "Different details from origin transaction",
|
|
151
|
+
944 => "Session error",
|
|
152
|
+
945 => "Duplicate transmission",
|
|
153
|
+
946 => "Cancellation of transaction while in progress",
|
|
154
|
+
947 => "Duplicate tranmission while in progress",
|
|
155
|
+
949 => "POS Inoperative",
|
|
156
|
+
950 => "Refund not possible",
|
|
157
|
+
9064 => "Card number incorrect",
|
|
158
|
+
9078 => "No payment method available",
|
|
159
|
+
9093 => "Non-existent card",
|
|
160
|
+
9218 => "Recursive transaction in bad gateway",
|
|
161
|
+
9253 => "Check-digit incorrect",
|
|
162
|
+
9256 => "Preauth not allowed for merchant",
|
|
163
|
+
9257 => "Preauth not allowed for card",
|
|
164
|
+
9261 => "Operating limit exceeded",
|
|
165
|
+
9912 => "Issuer not available",
|
|
166
|
+
9913 => "Confirmation error",
|
|
167
|
+
9914 => "KO Confirmation"
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
# Creates a new instance
|
|
171
|
+
#
|
|
172
|
+
# Redsys requires a login and secret_key, and optionally also accepts a
|
|
173
|
+
# non-default terminal.
|
|
174
|
+
#
|
|
175
|
+
# ==== Options
|
|
176
|
+
#
|
|
177
|
+
# * <tt>:login</tt> -- The Redsys Merchant ID (REQUIRED)
|
|
178
|
+
# * <tt>:secret_key</tt> -- The Redsys Secret Key. (REQUIRED)
|
|
179
|
+
# * <tt>:terminal</tt> -- The Redsys Terminal. Defaults to 1. (OPTIONAL)
|
|
180
|
+
# * <tt>:test</tt> -- +true+ or +false+. Defaults to +false+. (OPTIONAL)
|
|
181
|
+
def initialize(options = {})
|
|
182
|
+
requires!(options, :login, :secret_key)
|
|
183
|
+
options[:terminal] ||= 1
|
|
184
|
+
super
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def purchase(money, creditcard, options = {})
|
|
188
|
+
requires!(options, :order_id)
|
|
189
|
+
|
|
190
|
+
data = {}
|
|
191
|
+
add_action(data, :purchase)
|
|
192
|
+
add_amount(data, money, options)
|
|
193
|
+
add_order(data, options[:order_id])
|
|
194
|
+
add_creditcard(data, creditcard)
|
|
195
|
+
|
|
196
|
+
commit data
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def authorize(money, creditcard, options = {})
|
|
200
|
+
requires!(options, :order_id)
|
|
201
|
+
|
|
202
|
+
data = {}
|
|
203
|
+
add_action(data, :authorize)
|
|
204
|
+
add_amount(data, money, options)
|
|
205
|
+
add_order(data, options[:order_id])
|
|
206
|
+
add_creditcard(data, creditcard)
|
|
207
|
+
|
|
208
|
+
commit data
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def capture(money, authorization, options = {})
|
|
212
|
+
data = {}
|
|
213
|
+
add_action(data, :capture)
|
|
214
|
+
add_amount(data, money, options)
|
|
215
|
+
order_id, _, _ = split_authorization(authorization)
|
|
216
|
+
add_order(data, order_id)
|
|
217
|
+
|
|
218
|
+
commit data
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def void(authorization, options = {})
|
|
222
|
+
data = {}
|
|
223
|
+
add_action(data, :cancel)
|
|
224
|
+
order_id, amount, currency = split_authorization(authorization)
|
|
225
|
+
add_amount(data, amount, :currency => currency)
|
|
226
|
+
add_order(data, order_id)
|
|
227
|
+
|
|
228
|
+
commit data
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def refund(money, authorization, options = {})
|
|
232
|
+
data = {}
|
|
233
|
+
add_action(data, :refund)
|
|
234
|
+
add_amount(data, money, options)
|
|
235
|
+
order_id, _, _ = split_authorization(authorization)
|
|
236
|
+
add_order(data, order_id)
|
|
237
|
+
|
|
238
|
+
commit data
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
private
|
|
242
|
+
|
|
243
|
+
def add_action(data, action)
|
|
244
|
+
data[:action] = transaction_code(action)
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def add_amount(data, money, options)
|
|
248
|
+
data[:amount] = amount(money).to_s
|
|
249
|
+
data[:currency] = currency_code(options[:currency] || currency(money))
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def add_order(data, order_id)
|
|
253
|
+
raise ArgumentError.new("Invalid order_id format") unless(/^\d{4}[\da-zA-Z]{0,8}$/ =~ order_id)
|
|
254
|
+
data[:order_id] = order_id
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def url
|
|
258
|
+
test? ? test_url : live_url
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def add_creditcard(data, card)
|
|
262
|
+
name = [card.first_name, card.last_name].join(' ').slice(0, 60)
|
|
263
|
+
year = sprintf("%.4i", card.year)
|
|
264
|
+
month = sprintf("%.2i", card.month)
|
|
265
|
+
data[:card] = {
|
|
266
|
+
:name => name,
|
|
267
|
+
:pan => card.number,
|
|
268
|
+
:date => "#{year[2..3]}#{month}",
|
|
269
|
+
:cvv => card.verification_value
|
|
270
|
+
}
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def commit(data)
|
|
274
|
+
headers = {
|
|
275
|
+
'Content-Type' => 'application/x-www-form-urlencoded'
|
|
276
|
+
}
|
|
277
|
+
xml = build_xml_request(data)
|
|
278
|
+
parse(ssl_post(url, "entrada=#{CGI.escape(xml)}", headers))
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def build_signature(data)
|
|
282
|
+
str = data[:amount] +
|
|
283
|
+
data[:order_id].to_s +
|
|
284
|
+
@options[:login].to_s +
|
|
285
|
+
data[:currency]
|
|
286
|
+
|
|
287
|
+
if card = data[:card]
|
|
288
|
+
str << card[:pan]
|
|
289
|
+
str << card[:cvv] if card[:cvv]
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
str << data[:action]
|
|
293
|
+
str << @options[:secret_key]
|
|
294
|
+
|
|
295
|
+
Digest::SHA1.hexdigest(str)
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def build_xml_request(data)
|
|
299
|
+
xml = Builder::XmlMarkup.new :indent => 2
|
|
300
|
+
xml.DATOSENTRADA do
|
|
301
|
+
# Basic elements
|
|
302
|
+
xml.DS_Version 0.1
|
|
303
|
+
xml.DS_MERCHANT_CURRENCY data[:currency]
|
|
304
|
+
xml.DS_MERCHANT_AMOUNT data[:amount]
|
|
305
|
+
xml.DS_MERCHANT_ORDER data[:order_id]
|
|
306
|
+
xml.DS_MERCHANT_TRANSACTIONTYPE data[:action]
|
|
307
|
+
xml.DS_MERCHANT_TERMINAL @options[:terminal]
|
|
308
|
+
xml.DS_MERCHANT_MERCHANTCODE @options[:login]
|
|
309
|
+
xml.DS_MERCHANT_MERCHANTSIGNATURE build_signature(data)
|
|
310
|
+
|
|
311
|
+
# Only when card is present
|
|
312
|
+
if data[:card]
|
|
313
|
+
xml.DS_MERCHANT_TITULAR data[:card][:name]
|
|
314
|
+
xml.DS_MERCHANT_PAN data[:card][:pan]
|
|
315
|
+
xml.DS_MERCHANT_EXPIRYDATE data[:card][:date]
|
|
316
|
+
xml.DS_MERCHANT_CVV2 data[:card][:cvv]
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
xml.target!
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def parse(data)
|
|
323
|
+
params = {}
|
|
324
|
+
success = false
|
|
325
|
+
message = ""
|
|
326
|
+
options = @options.merge(:test => test?)
|
|
327
|
+
xml = Nokogiri::XML(data)
|
|
328
|
+
code = xml.xpath("//RETORNOXML/CODIGO").text
|
|
329
|
+
if code == "0"
|
|
330
|
+
op = xml.xpath("//RETORNOXML/OPERACION")
|
|
331
|
+
op.children.each do |element|
|
|
332
|
+
params[element.name.downcase.to_sym] = element.text
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
if validate_signature(params)
|
|
336
|
+
message = response_text(params[:ds_response])
|
|
337
|
+
options[:authorization] = build_authorization(params)
|
|
338
|
+
success = is_success_response?(params[:ds_response])
|
|
339
|
+
else
|
|
340
|
+
message = "Response failed validation check"
|
|
341
|
+
end
|
|
342
|
+
else
|
|
343
|
+
# Some kind of programmer error with the request!
|
|
344
|
+
message = "#{code} ERROR"
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
Response.new(success, message, params, options)
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
def validate_signature(data)
|
|
351
|
+
str = data[:ds_amount] +
|
|
352
|
+
data[:ds_order].to_s +
|
|
353
|
+
data[:ds_merchantcode] +
|
|
354
|
+
data[:ds_currency] +
|
|
355
|
+
data[:ds_response] +
|
|
356
|
+
data[:ds_cardnumber].to_s +
|
|
357
|
+
data[:ds_transactiontype].to_s +
|
|
358
|
+
data[:ds_securepayment].to_s +
|
|
359
|
+
@options[:secret_key]
|
|
360
|
+
|
|
361
|
+
sig = Digest::SHA1.hexdigest(str)
|
|
362
|
+
data[:ds_signature].to_s.downcase == sig
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
def build_authorization(params)
|
|
366
|
+
[params[:ds_order], params[:ds_amount], params[:ds_currency]].join("|")
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
def split_authorization(authorization)
|
|
370
|
+
order_id, amount, currency = authorization.split("|")
|
|
371
|
+
[order_id, amount.to_i, currency]
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
def currency_code(currency)
|
|
375
|
+
return currency if currency =~ /^\d+$/
|
|
376
|
+
CURRENCY_CODES[currency]
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
def transaction_code(type)
|
|
380
|
+
SUPPORTED_TRANSACTIONS[type]
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
def response_text(code)
|
|
384
|
+
code = code.to_i
|
|
385
|
+
code = 0 if code < 100
|
|
386
|
+
RESPONSE_TEXTS[code] || "Unkown code, please check in manual"
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
def is_success_response?(code)
|
|
390
|
+
(code.to_i < 100) || [400, 481, 500, 900].include?(code.to_i)
|
|
391
|
+
end
|
|
392
|
+
end
|
|
393
|
+
end
|
|
394
|
+
end
|
|
@@ -7,13 +7,13 @@ module ActiveMerchant #:nodoc:
|
|
|
7
7
|
base.homepage_url = 'http://www.sagepayments.com'
|
|
8
8
|
base.display_name = 'Sage Payment Solutions'
|
|
9
9
|
end
|
|
10
|
-
|
|
10
|
+
|
|
11
11
|
# Transactions types:
|
|
12
12
|
# <tt>01</tt> - Sale
|
|
13
|
-
# <tt>02</tt> - AuthOnly
|
|
14
|
-
# <tt>03</tt> - Force/PriorAuthSale
|
|
15
|
-
# <tt>04</tt> - Void
|
|
16
|
-
# <tt>06</tt> - Credit
|
|
13
|
+
# <tt>02</tt> - AuthOnly
|
|
14
|
+
# <tt>03</tt> - Force/PriorAuthSale
|
|
15
|
+
# <tt>04</tt> - Void
|
|
16
|
+
# <tt>06</tt> - Credit
|
|
17
17
|
# <tt>11</tt> - PriorAuthSale by Reference*
|
|
18
18
|
TRANSACTIONS = {
|
|
19
19
|
:purchase => '01',
|
|
@@ -22,83 +22,82 @@ module ActiveMerchant #:nodoc:
|
|
|
22
22
|
:void => '04',
|
|
23
23
|
:credit => '06'
|
|
24
24
|
}
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
def initialize(options = {})
|
|
27
27
|
requires!(options, :login, :password)
|
|
28
|
-
@options = options
|
|
29
28
|
super
|
|
30
29
|
end
|
|
31
|
-
|
|
30
|
+
|
|
32
31
|
private
|
|
33
32
|
def add_invoice(post, options)
|
|
34
33
|
post[:T_ordernum] = options[:order_id].slice(0, 20)
|
|
35
34
|
post[:T_tax] = amount(options[:tax]) unless options[:tax].blank?
|
|
36
35
|
post[:T_shipping] = amount(options[:shipping]) unless options[:shipping].blank?
|
|
37
36
|
end
|
|
38
|
-
|
|
37
|
+
|
|
39
38
|
def add_reference(post, reference)
|
|
40
39
|
ref, source = reference.to_s.split(";")
|
|
41
40
|
post[:T_reference] = ref
|
|
42
41
|
end
|
|
43
|
-
|
|
42
|
+
|
|
44
43
|
def add_amount(post, money)
|
|
45
44
|
post[:T_amt] = amount(money)
|
|
46
45
|
end
|
|
47
|
-
|
|
46
|
+
|
|
48
47
|
def add_customer_data(post, options)
|
|
49
48
|
post[:T_customer_number] = options[:customer]
|
|
50
49
|
end
|
|
51
50
|
|
|
52
51
|
def add_addresses(post, options)
|
|
53
52
|
billing_address = options[:billing_address] || options[:address] || {}
|
|
54
|
-
|
|
53
|
+
|
|
55
54
|
post[:C_address] = billing_address[:address1]
|
|
56
55
|
post[:C_city] = billing_address[:city]
|
|
57
|
-
|
|
56
|
+
|
|
58
57
|
if ['US', 'CA'].include?(billing_address[:country])
|
|
59
58
|
post[:C_state] = billing_address[:state]
|
|
60
59
|
else
|
|
61
60
|
post[:C_state] = "Outside of United States"
|
|
62
61
|
end
|
|
63
|
-
|
|
62
|
+
|
|
64
63
|
post[:C_zip] = billing_address[:zip]
|
|
65
|
-
post[:C_country] = billing_address[:country]
|
|
64
|
+
post[:C_country] = billing_address[:country]
|
|
66
65
|
post[:C_telephone] = billing_address[:phone]
|
|
67
66
|
post[:C_fax] = billing_address[:fax]
|
|
68
67
|
post[:C_email] = options[:email]
|
|
69
|
-
|
|
68
|
+
|
|
70
69
|
if shipping_address = options[:shipping_address]
|
|
71
70
|
post[:C_ship_name] = shipping_address[:name]
|
|
72
71
|
post[:C_ship_address] = shipping_address[:address1]
|
|
73
72
|
post[:C_ship_city] = shipping_address[:city]
|
|
74
73
|
post[:C_ship_state] = shipping_address[:state]
|
|
75
|
-
post[:C_ship_zip] = shipping_address[:zip]
|
|
74
|
+
post[:C_ship_zip] = shipping_address[:zip]
|
|
76
75
|
post[:C_ship_country] = shipping_address[:country]
|
|
77
76
|
end
|
|
78
77
|
end
|
|
79
|
-
|
|
78
|
+
|
|
80
79
|
def add_transaction_data(post, money, options)
|
|
81
80
|
add_amount(post, money)
|
|
82
81
|
add_invoice(post, options)
|
|
83
|
-
add_addresses(post, options)
|
|
82
|
+
add_addresses(post, options)
|
|
84
83
|
add_customer_data(post, options)
|
|
85
84
|
end
|
|
86
|
-
|
|
85
|
+
|
|
87
86
|
def commit(action, params)
|
|
88
87
|
response = parse(ssl_post(self.live_url, post_data(action, params)))
|
|
89
|
-
|
|
90
|
-
Response.new(success?(response), response[:message], response,
|
|
91
|
-
:test => test?,
|
|
88
|
+
|
|
89
|
+
Response.new(success?(response), response[:message], response,
|
|
90
|
+
:test => test?,
|
|
92
91
|
:authorization => authorization_from(response),
|
|
93
92
|
:avs_result => { :code => response[:avs_result] },
|
|
94
93
|
:cvv_result => response[:cvv_result]
|
|
95
94
|
)
|
|
96
95
|
end
|
|
97
|
-
|
|
96
|
+
|
|
98
97
|
def authorization_from(response)
|
|
99
98
|
"#{response[:reference]};#{source}"
|
|
100
99
|
end
|
|
101
|
-
|
|
100
|
+
|
|
102
101
|
def success?(response)
|
|
103
102
|
response[:success] == 'A'
|
|
104
103
|
end
|
|
@@ -107,7 +106,7 @@ module ActiveMerchant #:nodoc:
|
|
|
107
106
|
params[:M_id] = @options[:login]
|
|
108
107
|
params[:M_key] = @options[:password]
|
|
109
108
|
params[:T_code] = TRANSACTIONS[action]
|
|
110
|
-
|
|
109
|
+
|
|
111
110
|
params.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&")
|
|
112
111
|
end
|
|
113
112
|
end
|