activemerchant 1.15.0 → 1.16.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.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
|