activemerchant 1.15.0 → 1.16.0
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +0 -0
- data/CHANGELOG +21 -0
- data/CONTRIBUTORS +13 -1
- data/lib/active_merchant.rb +2 -5
- data/lib/active_merchant/billing/avs_result.rb +2 -2
- data/lib/active_merchant/billing/credit_card.rb +6 -0
- data/lib/active_merchant/billing/gateways/authorize_net.rb +1 -1
- data/lib/active_merchant/billing/gateways/barclays_epdq.rb +1 -2
- data/lib/active_merchant/billing/gateways/bogus.rb +34 -24
- data/lib/active_merchant/billing/gateways/braintree/braintree_common.rb +1 -1
- data/lib/active_merchant/billing/gateways/cyber_source.rb +20 -0
- data/lib/active_merchant/billing/gateways/moneris.rb +1 -1
- data/lib/active_merchant/billing/gateways/orbital.rb +6 -2
- data/lib/active_merchant/billing/gateways/payment_express.rb +1 -1
- data/lib/active_merchant/billing/gateways/paystation.rb +201 -0
- data/lib/active_merchant/billing/gateways/qbms.rb +1 -3
- data/lib/active_merchant/billing/gateways/quickpay.rb +56 -51
- data/lib/active_merchant/billing/gateways/stripe.rb +212 -0
- data/lib/active_merchant/billing/gateways/worldpay.rb +1 -1
- data/lib/active_merchant/billing/integrations/action_view_helper.rb +2 -2
- data/lib/active_merchant/billing/integrations/e_payment_plans.rb +48 -0
- data/lib/active_merchant/billing/integrations/e_payment_plans/helper.rb +34 -0
- data/lib/active_merchant/billing/integrations/e_payment_plans/notification.rb +84 -0
- data/lib/active_merchant/billing/integrations/helper.rb +3 -3
- data/lib/active_merchant/billing/integrations/moneybookers/helper.rb +11 -0
- data/lib/active_merchant/billing/integrations/valitor/response_fields.rb +16 -7
- data/lib/active_merchant/billing/integrations/world_pay.rb +4 -15
- data/lib/active_merchant/version.rb +1 -1
- metadata +46 -21
- metadata.gz.sig +0 -0
- data/README.rdoc +0 -164
@@ -6,50 +6,51 @@ module ActiveMerchant #:nodoc:
|
|
6
6
|
class QuickpayGateway < Gateway
|
7
7
|
URL = 'https://secure.quickpay.dk/api'
|
8
8
|
|
9
|
-
self.default_currency = 'DKK'
|
9
|
+
self.default_currency = 'DKK'
|
10
10
|
self.money_format = :cents
|
11
11
|
self.supported_cardtypes = [ :dankort, :forbrugsforeningen, :visa, :master, :american_express, :diners_club, :jcb, :maestro ]
|
12
12
|
self.supported_countries = ['DK', 'SE']
|
13
13
|
self.homepage_url = 'http://quickpay.dk/'
|
14
14
|
self.display_name = 'Quickpay'
|
15
|
-
|
15
|
+
|
16
16
|
PROTOCOL = 3
|
17
|
-
|
17
|
+
|
18
18
|
MD5_CHECK_FIELDS = {
|
19
|
-
:authorize => [:protocol, :msgtype, :merchant, :ordernumber, :amount, :currency, :autocapture, :cardnumber, :expirationdate, :cvd, :cardtypelock],
|
19
|
+
:authorize => [:protocol, :msgtype, :merchant, :ordernumber, :amount, :currency, :autocapture, :cardnumber, :expirationdate, :cvd, :cardtypelock, :testmode],
|
20
20
|
:capture => [:protocol, :msgtype, :merchant, :amount, :transaction],
|
21
21
|
:cancel => [:protocol, :msgtype, :merchant, :transaction],
|
22
22
|
:refund => [:protocol, :msgtype, :merchant, :amount, :transaction],
|
23
|
-
:subscribe => [:protocol, :msgtype, :merchant, :ordernumber, :cardnumber, :expirationdate, :cvd, :cardtypelock, :description],
|
23
|
+
:subscribe => [:protocol, :msgtype, :merchant, :ordernumber, :cardnumber, :expirationdate, :cvd, :cardtypelock, :description, :testmode],
|
24
24
|
:recurring => [:protocol, :msgtype, :merchant, :ordernumber, :amount, :currency, :autocapture, :transaction],
|
25
25
|
:status => [:protocol, :msgtype, :merchant, :transaction],
|
26
26
|
:chstatus => [:protocol, :msgtype, :merchant],
|
27
27
|
}
|
28
|
-
|
28
|
+
|
29
29
|
APPROVED = '000'
|
30
|
-
|
30
|
+
|
31
31
|
# The login is the QuickpayId
|
32
32
|
# The password is the md5checkword from the Quickpay admin interface
|
33
33
|
def initialize(options = {})
|
34
34
|
requires!(options, :login, :password)
|
35
35
|
@options = options
|
36
36
|
super
|
37
|
-
end
|
38
|
-
|
37
|
+
end
|
38
|
+
|
39
39
|
def authorize(money, credit_card_or_reference, options = {})
|
40
40
|
post = {}
|
41
|
-
|
41
|
+
|
42
42
|
add_amount(post, money, options)
|
43
43
|
add_invoice(post, options)
|
44
44
|
add_creditcard_or_reference(post, credit_card_or_reference, options)
|
45
45
|
add_autocapture(post, false)
|
46
|
+
add_testmode(post)
|
46
47
|
|
47
48
|
commit(recurring_or_authorize(credit_card_or_reference), post)
|
48
49
|
end
|
49
|
-
|
50
|
+
|
50
51
|
def purchase(money, credit_card_or_reference, options = {})
|
51
52
|
post = {}
|
52
|
-
|
53
|
+
|
53
54
|
add_amount(post, money, options)
|
54
55
|
add_creditcard_or_reference(post, credit_card_or_reference, options)
|
55
56
|
add_invoice(post, options)
|
@@ -57,24 +58,24 @@ module ActiveMerchant #:nodoc:
|
|
57
58
|
|
58
59
|
commit(recurring_or_authorize(credit_card_or_reference), post)
|
59
60
|
end
|
60
|
-
|
61
|
+
|
61
62
|
def capture(money, authorization, options = {})
|
62
63
|
post = {}
|
63
|
-
|
64
|
+
|
64
65
|
add_reference(post, authorization)
|
65
66
|
add_amount_without_currency(post, money)
|
66
|
-
|
67
|
+
|
67
68
|
commit(:capture, post)
|
68
69
|
end
|
69
|
-
|
70
|
+
|
70
71
|
def void(identification, options = {})
|
71
72
|
post = {}
|
72
|
-
|
73
|
+
|
73
74
|
add_reference(post, identification)
|
74
|
-
|
75
|
+
|
75
76
|
commit(:cancel, post)
|
76
77
|
end
|
77
|
-
|
78
|
+
|
78
79
|
def refund(money, identification, options = {})
|
79
80
|
post = {}
|
80
81
|
|
@@ -86,57 +87,58 @@ module ActiveMerchant #:nodoc:
|
|
86
87
|
|
87
88
|
def credit(money, identification, options = {})
|
88
89
|
deprecated CREDIT_DEPRECATION_MESSAGE
|
89
|
-
refund(money, identification, options)
|
90
|
+
refund(money, identification, options)
|
90
91
|
end
|
91
|
-
|
92
|
-
def store(creditcard, options = {})
|
92
|
+
|
93
|
+
def store(creditcard, options = {})
|
93
94
|
post = {}
|
94
|
-
|
95
|
+
|
95
96
|
add_creditcard(post, creditcard, options)
|
96
97
|
add_invoice(post, options)
|
97
98
|
add_description(post, options)
|
99
|
+
add_testmode(post)
|
98
100
|
|
99
101
|
commit(:subscribe, post)
|
100
102
|
end
|
101
|
-
|
102
|
-
private
|
103
|
-
|
103
|
+
|
104
|
+
private
|
105
|
+
|
104
106
|
def add_amount(post, money, options = {})
|
105
107
|
post[:amount] = amount(money)
|
106
108
|
post[:currency] = options[:currency] || currency(money)
|
107
109
|
end
|
108
|
-
|
110
|
+
|
109
111
|
def add_amount_without_currency(post, money, options = {})
|
110
112
|
post[:amount] = amount(money)
|
111
113
|
end
|
112
|
-
|
114
|
+
|
113
115
|
def add_invoice(post, options)
|
114
116
|
post[:ordernumber] = format_order_number(options[:order_id])
|
115
117
|
end
|
116
|
-
|
118
|
+
|
117
119
|
def add_creditcard(post, credit_card, options)
|
118
|
-
post[:cardnumber] = credit_card.number
|
120
|
+
post[:cardnumber] = credit_card.number
|
119
121
|
post[:cvd] = credit_card.verification_value
|
120
122
|
post[:expirationdate] = expdate(credit_card)
|
121
123
|
post[:cardtypelock] = options[:cardtypelock] unless options[:cardtypelock].blank?
|
122
124
|
end
|
123
|
-
|
125
|
+
|
124
126
|
def add_reference(post, identification)
|
125
127
|
post[:transaction] = identification
|
126
128
|
end
|
127
|
-
|
129
|
+
|
128
130
|
def add_creditcard_or_reference(post, credit_card_or_reference, options)
|
129
131
|
if credit_card_or_reference.is_a?(String)
|
130
132
|
add_reference(post, credit_card_or_reference)
|
131
133
|
else
|
132
134
|
add_creditcard(post, credit_card_or_reference, options)
|
133
135
|
end
|
134
|
-
end
|
135
|
-
|
136
|
+
end
|
137
|
+
|
136
138
|
def add_autocapture(post, autocapture)
|
137
139
|
post[:autocapture] = autocapture ? 1 : 0
|
138
140
|
end
|
139
|
-
|
141
|
+
|
140
142
|
def recurring_or_authorize(credit_card_or_reference)
|
141
143
|
credit_card_or_reference.is_a?(String) ? :recurring : :authorize
|
142
144
|
end
|
@@ -144,29 +146,33 @@ module ActiveMerchant #:nodoc:
|
|
144
146
|
def add_description(post, options)
|
145
147
|
post[:description] = options[:description]
|
146
148
|
end
|
147
|
-
|
149
|
+
|
150
|
+
def add_testmode(post)
|
151
|
+
post[:testmode] = test? ? '1' : '0'
|
152
|
+
end
|
153
|
+
|
148
154
|
def commit(action, params)
|
149
155
|
response = parse(ssl_post(URL, post_data(action, params)))
|
150
|
-
|
151
|
-
Response.new(successful?(response), message_from(response), response,
|
152
|
-
:test => test?,
|
156
|
+
|
157
|
+
Response.new(successful?(response), message_from(response), response,
|
158
|
+
:test => test?,
|
153
159
|
:authorization => response[:transaction]
|
154
160
|
)
|
155
161
|
end
|
156
|
-
|
162
|
+
|
157
163
|
def successful?(response)
|
158
164
|
response[:qpstat] == APPROVED
|
159
165
|
end
|
160
166
|
|
161
167
|
def parse(data)
|
162
168
|
response = {}
|
163
|
-
|
169
|
+
|
164
170
|
doc = REXML::Document.new(data)
|
165
|
-
|
171
|
+
|
166
172
|
doc.root.elements.each do |element|
|
167
173
|
response[element.name.to_sym] = element.text
|
168
174
|
end
|
169
|
-
|
175
|
+
|
170
176
|
response
|
171
177
|
end
|
172
178
|
|
@@ -175,39 +181,38 @@ module ActiveMerchant #:nodoc:
|
|
175
181
|
when '008' # Error in request data
|
176
182
|
response[:qpstatmsg].to_s
|
177
183
|
#.scan(/[A-Z][a-z0-9 \/]+/).to_sentence
|
178
|
-
else
|
184
|
+
else
|
179
185
|
response[:qpstatmsg].to_s
|
180
186
|
end
|
181
187
|
end
|
182
|
-
|
188
|
+
|
183
189
|
def post_data(action, params = {})
|
184
190
|
params[:protocol] = PROTOCOL
|
185
191
|
params[:msgtype] = action.to_s
|
186
192
|
params[:merchant] = @options[:login]
|
187
|
-
#params[:testmode] = '1' if test?
|
188
193
|
params[:md5check] = generate_check_hash(action, params)
|
189
|
-
|
194
|
+
|
190
195
|
params.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&")
|
191
196
|
end
|
192
|
-
|
197
|
+
|
193
198
|
def generate_check_hash(action, params)
|
194
199
|
string = MD5_CHECK_FIELDS[action].collect do |key|
|
195
200
|
params[key]
|
196
201
|
end.join('')
|
197
|
-
|
202
|
+
|
198
203
|
# Add the md5checkword
|
199
204
|
string << @options[:password].to_s
|
200
205
|
|
201
206
|
Digest::MD5.hexdigest(string)
|
202
207
|
end
|
203
|
-
|
208
|
+
|
204
209
|
def expdate(credit_card)
|
205
210
|
year = format(credit_card.year, :two_digits)
|
206
211
|
month = format(credit_card.month, :two_digits)
|
207
212
|
|
208
213
|
"#{year}#{month}"
|
209
214
|
end
|
210
|
-
|
215
|
+
|
211
216
|
# Limited to 20 digits max
|
212
217
|
def format_order_number(number)
|
213
218
|
number.to_s.gsub(/[^\w_]/, '').rjust(4, "0")[0...20]
|
@@ -0,0 +1,212 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module ActiveMerchant #:nodoc:
|
4
|
+
module Billing #:nodoc:
|
5
|
+
class StripeGateway < Gateway
|
6
|
+
LIVE_URL = 'https://api.stripe.com/v1/'
|
7
|
+
|
8
|
+
AVS_CODE_TRANSLATOR = {
|
9
|
+
'line1: pass, zip: pass' => 'Y',
|
10
|
+
'line1: pass, zip: fail' => 'A',
|
11
|
+
'line1: pass, zip: unchecked' => 'B',
|
12
|
+
'line1: fail, zip: pass' => 'Z',
|
13
|
+
'line1: fail, zip: fail' => 'N',
|
14
|
+
'line1: unchecked, zip: pass' => 'P',
|
15
|
+
'line1: unchecked, zip: unchecked' => 'I'
|
16
|
+
}
|
17
|
+
|
18
|
+
CVC_CODE_TRANSLATOR = {
|
19
|
+
'pass' => 'M',
|
20
|
+
'fail' => 'N',
|
21
|
+
'unchecked' => 'P'
|
22
|
+
}
|
23
|
+
|
24
|
+
self.supported_countries = ['US']
|
25
|
+
self.default_currency = 'USD'
|
26
|
+
self.money_format = :cents
|
27
|
+
self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :diners_club]
|
28
|
+
|
29
|
+
self.homepage_url = 'https://stripe.com/'
|
30
|
+
self.display_name = 'Stripe'
|
31
|
+
|
32
|
+
def initialize(options = {})
|
33
|
+
requires!(options, :login)
|
34
|
+
@api_key = options[:login]
|
35
|
+
super
|
36
|
+
end
|
37
|
+
|
38
|
+
def purchase(money, creditcard, options = {})
|
39
|
+
post = {}
|
40
|
+
|
41
|
+
add_amount(post, money, options)
|
42
|
+
add_creditcard(post, creditcard, options)
|
43
|
+
add_customer(post, options)
|
44
|
+
add_customer_data(post, options)
|
45
|
+
add_flags(post, options)
|
46
|
+
|
47
|
+
raise ArgumentError.new("Customer or Credit Card required.") if !post[:card] && !post[:customer]
|
48
|
+
|
49
|
+
commit('charges', post)
|
50
|
+
end
|
51
|
+
|
52
|
+
def authorize(money, creditcard, options = {})
|
53
|
+
purchase(money, creditcard, options.merge(:uncaptured => true))
|
54
|
+
end
|
55
|
+
|
56
|
+
def capture(money, identification, options = {})
|
57
|
+
commit("charges/#{CGI.escape(identification)}/capture", {})
|
58
|
+
end
|
59
|
+
|
60
|
+
def void(identification, options={})
|
61
|
+
commit("charges/#{CGI.escape(identification)}/refund", {})
|
62
|
+
end
|
63
|
+
|
64
|
+
def refund(money, identification, options = {})
|
65
|
+
post = {}
|
66
|
+
|
67
|
+
post[:amount] = amount(money) if money
|
68
|
+
|
69
|
+
commit("charges/#{CGI.escape(identification)}/refund", post)
|
70
|
+
end
|
71
|
+
|
72
|
+
def store(creditcard, options={})
|
73
|
+
post = {}
|
74
|
+
add_creditcard(post, creditcard, options)
|
75
|
+
add_customer_data(post, options)
|
76
|
+
|
77
|
+
if options[:customer]
|
78
|
+
commit("customers/#{CGI.escape(options[:customer])}", post)
|
79
|
+
else
|
80
|
+
commit('customers', post)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def add_amount(post, money, options)
|
87
|
+
post[:amount] = amount(money)
|
88
|
+
post[:currency] = (options[:currency] || currency(money)).downcase
|
89
|
+
end
|
90
|
+
|
91
|
+
def add_customer_data(post, options)
|
92
|
+
post[:description] = options[:email] || options[:description]
|
93
|
+
end
|
94
|
+
|
95
|
+
def add_address(post, options)
|
96
|
+
return unless post[:card] && post[:card].kind_of?(Hash)
|
97
|
+
if address = options[:billing_address] || options[:address]
|
98
|
+
post[:card][:address_line1] = address[:address1] if address[:address1]
|
99
|
+
post[:card][:address_line2] = address[:address2] if address[:address2]
|
100
|
+
post[:card][:address_country] = address[:country] if address[:country]
|
101
|
+
post[:card][:address_zip] = address[:zip] if address[:zip]
|
102
|
+
post[:card][:address_state] = address[:state] if address[:state]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def add_creditcard(post, creditcard, options)
|
107
|
+
if creditcard.respond_to?(:number)
|
108
|
+
card = {}
|
109
|
+
card[:number] = creditcard.number
|
110
|
+
card[:exp_month] = creditcard.month
|
111
|
+
card[:exp_year] = creditcard.year
|
112
|
+
card[:cvc] = creditcard.verification_value if creditcard.verification_value?
|
113
|
+
card[:name] = creditcard.name if creditcard.name
|
114
|
+
post[:card] = card
|
115
|
+
|
116
|
+
add_address(post, options)
|
117
|
+
elsif creditcard.kind_of?(String)
|
118
|
+
post[:card] = creditcard
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def add_customer(post, options)
|
123
|
+
post[:customer] = options[:customer] if options[:customer] && !post[:card]
|
124
|
+
end
|
125
|
+
|
126
|
+
def add_flags(post, options)
|
127
|
+
post[:uncaptured] = true if options[:uncaptured]
|
128
|
+
end
|
129
|
+
|
130
|
+
def parse(body)
|
131
|
+
JSON.parse(body)
|
132
|
+
end
|
133
|
+
|
134
|
+
def post_data(params)
|
135
|
+
params.map do |key, value|
|
136
|
+
next if value.blank?
|
137
|
+
if value.is_a?(Hash)
|
138
|
+
h = {}
|
139
|
+
value.each do |k, v|
|
140
|
+
h["#{key}[#{k}]"] = v unless v.blank?
|
141
|
+
end
|
142
|
+
post_data(h)
|
143
|
+
else
|
144
|
+
"#{key}=#{CGI.escape(value.to_s)}"
|
145
|
+
end
|
146
|
+
end.compact.join("&")
|
147
|
+
end
|
148
|
+
|
149
|
+
def headers
|
150
|
+
@@ua ||= JSON.dump({
|
151
|
+
:bindings_version => ActiveMerchant::VERSION,
|
152
|
+
:lang => 'ruby',
|
153
|
+
:lang_version => "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})",
|
154
|
+
:platform => RUBY_PLATFORM,
|
155
|
+
:publisher => 'active_merchant',
|
156
|
+
:uname => (RUBY_PLATFORM =~ /linux|darwin/i ? `uname -a 2>/dev/null`.strip : nil)
|
157
|
+
})
|
158
|
+
|
159
|
+
{
|
160
|
+
"Authorization" => "Basic " + ActiveSupport::Base64.encode64(@api_key.to_s + ":").strip,
|
161
|
+
"User-Agent" => "Stripe/v1 ActiveMerchantBindings/#{ActiveMerchant::VERSION}",
|
162
|
+
"X-Stripe-Client-User-Agent" => @@ua
|
163
|
+
}
|
164
|
+
end
|
165
|
+
|
166
|
+
def commit(url, parameters, method=:post)
|
167
|
+
raw_response = response = nil
|
168
|
+
success = false
|
169
|
+
begin
|
170
|
+
raw_response = ssl_request(method, LIVE_URL + url, post_data(parameters), headers)
|
171
|
+
response = parse(raw_response)
|
172
|
+
success = !response.key?("error")
|
173
|
+
rescue ResponseError => e
|
174
|
+
raw_response = e.response.body
|
175
|
+
response = response_error(raw_response)
|
176
|
+
rescue JSON::ParserError
|
177
|
+
response = json_error(raw_response)
|
178
|
+
end
|
179
|
+
|
180
|
+
card = response["card"] || {}
|
181
|
+
avs_code = AVS_CODE_TRANSLATOR["line1: #{card["address_line1_check"]}, zip: #{card["address_zip_check"]}"]
|
182
|
+
cvc_code = CVC_CODE_TRANSLATOR[card["cvc_check"]]
|
183
|
+
Response.new(success,
|
184
|
+
success ? "Transaction approved" : response["error"]["message"],
|
185
|
+
response,
|
186
|
+
:test => !response["livemode"],
|
187
|
+
:authorization => response["id"],
|
188
|
+
:avs_result => { :code => avs_code },
|
189
|
+
:cvv_result => cvc_code
|
190
|
+
)
|
191
|
+
end
|
192
|
+
|
193
|
+
def response_error(raw_response)
|
194
|
+
begin
|
195
|
+
parse(raw_response)
|
196
|
+
rescue JSON::ParserError
|
197
|
+
json_error(raw_response)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def json_error(raw_response)
|
202
|
+
msg = 'Invalid response received from the Stripe API. Please contact support@stripe.com if you continue to receive this message.'
|
203
|
+
msg += " (The raw response returned by the API was #{raw_response.inspect})"
|
204
|
+
{
|
205
|
+
"error" => {
|
206
|
+
"message" => msg
|
207
|
+
}
|
208
|
+
}
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|