yetanothernguyen-activemerchant 1.16.0 → 1.21.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 +95 -0
- data/CONTRIBUTORS +29 -0
- data/lib/active_merchant/billing/credit_card.rb +105 -19
- data/lib/active_merchant/billing/credit_card_methods.rb +5 -1
- data/lib/active_merchant/billing/gateway.rb +1 -1
- data/lib/active_merchant/billing/gateways/authorize_net.rb +24 -2
- data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +104 -18
- data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +110 -4
- data/lib/active_merchant/billing/gateways/beanstream.rb +29 -1
- data/lib/active_merchant/billing/gateways/braintree_blue.rb +9 -4
- data/lib/active_merchant/billing/gateways/braintree_orange.rb +4 -0
- data/lib/active_merchant/billing/gateways/card_save.rb +23 -0
- data/lib/active_merchant/billing/gateways/certo_direct.rb +279 -0
- data/lib/active_merchant/billing/gateways/efsnet.rb +9 -9
- data/lib/active_merchant/billing/gateways/elavon.rb +2 -1
- data/lib/active_merchant/billing/gateways/epay.rb +12 -6
- data/lib/active_merchant/billing/gateways/eway_managed.rb +46 -12
- data/lib/active_merchant/billing/gateways/exact.rb +5 -0
- data/lib/active_merchant/billing/gateways/ideal/ideal_base.rb +3 -3
- data/lib/active_merchant/billing/gateways/ipay88.rb +157 -0
- data/lib/active_merchant/billing/gateways/iridium.rb +3 -3
- data/lib/active_merchant/billing/gateways/itransact.rb +450 -0
- data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +1 -0
- data/lib/active_merchant/billing/gateways/moneris.rb +4 -0
- data/lib/active_merchant/billing/gateways/nab_transact.rb +244 -0
- data/lib/active_merchant/billing/gateways/ogone.rb +94 -56
- data/lib/active_merchant/billing/gateways/optimal_payment.rb +277 -0
- data/lib/active_merchant/billing/gateways/orbital.rb +57 -34
- data/lib/active_merchant/billing/gateways/pay_junction.rb +6 -1
- data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +1 -0
- data/lib/active_merchant/billing/gateways/payflow/payflow_express_response.rb +2 -2
- data/lib/active_merchant/billing/gateways/payflow.rb +10 -2
- data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +8 -5
- data/lib/active_merchant/billing/gateways/paypal_digital_goods.rb +43 -0
- data/lib/active_merchant/billing/gateways/paypal_express.rb +93 -40
- data/lib/active_merchant/billing/gateways/paypal_express_common.rb +8 -3
- data/lib/active_merchant/billing/gateways/qbms.rb +4 -0
- data/lib/active_merchant/billing/gateways/quickpay.rb +97 -22
- data/lib/active_merchant/billing/gateways/realex.rb +5 -1
- data/lib/active_merchant/billing/gateways/samurai.rb +121 -0
- data/lib/active_merchant/billing/gateways/secure_pay_au.rb +136 -49
- data/lib/active_merchant/billing/gateways/secure_pay_tech.rb +1 -1
- data/lib/active_merchant/billing/gateways/skip_jack.rb +7 -2
- data/lib/active_merchant/billing/gateways/stripe.rb +51 -19
- data/lib/active_merchant/billing/gateways/usa_epay.rb +13 -184
- data/lib/active_merchant/billing/gateways/usa_epay_advanced.rb +1496 -0
- data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +206 -0
- data/lib/active_merchant/billing/gateways/verifi.rb +2 -2
- data/lib/active_merchant/billing/gateways/viaklix.rb +1 -1
- data/lib/active_merchant/billing/gateways/worldpay.rb +1 -1
- data/lib/active_merchant/billing/integrations/action_view_helper.rb +6 -2
- data/lib/active_merchant/billing/integrations/authorize_net_sim/helper.rb +228 -0
- data/lib/active_merchant/billing/integrations/authorize_net_sim/notification.rb +340 -0
- data/lib/active_merchant/billing/integrations/authorize_net_sim.rb +38 -0
- data/lib/active_merchant/billing/integrations/direc_pay/helper.rb +4 -4
- data/lib/active_merchant/billing/integrations/dwolla/helper.rb +31 -0
- data/lib/active_merchant/billing/integrations/dwolla/notification.rb +55 -0
- data/lib/active_merchant/billing/integrations/dwolla/return.rb +38 -0
- data/lib/active_merchant/billing/integrations/dwolla.rb +30 -0
- data/lib/active_merchant/billing/integrations/helper.rb +19 -2
- data/lib/active_merchant/billing/integrations/ipay88/helper.rb +120 -0
- data/lib/active_merchant/billing/integrations/ipay88/return.rb +121 -0
- data/lib/active_merchant/billing/integrations/ipay88.rb +40 -0
- data/lib/active_merchant/billing/integrations/nochex.rb +1 -1
- data/lib/active_merchant/billing/integrations/payflow_link/helper.rb +100 -0
- data/lib/active_merchant/billing/integrations/payflow_link/notification.rb +78 -0
- data/lib/active_merchant/billing/integrations/payflow_link.rb +21 -0
- data/lib/active_merchant/billing/integrations/sage_pay_form/encryption.rb +4 -4
- data/lib/active_merchant/billing/integrations/sage_pay_form/helper.rb +18 -2
- data/lib/active_merchant/billing/integrations/two_checkout.rb +1 -2
- data/lib/active_merchant/railtie.rb +7 -7
- data/lib/active_merchant/railtie.rb.orig +19 -0
- data/lib/active_merchant/version.rb +1 -1
- data/lib/active_merchant.rb +23 -10
- data/lib/active_merchant.rb.orig +78 -0
- metadata +147 -63
- data/lib/active_merchant/common/connection.rb +0 -177
- data/lib/active_merchant/common/country.rb +0 -328
- data/lib/active_merchant/common/error.rb +0 -26
- data/lib/active_merchant/common/post_data.rb +0 -24
- data/lib/active_merchant/common/posts_data.rb +0 -63
- data/lib/active_merchant/common/requires_parameters.rb +0 -16
- data/lib/active_merchant/common/utils.rb +0 -22
- data/lib/active_merchant/common/validateable.rb +0 -81
- data/lib/active_merchant/common.rb +0 -14
- data/lib/certs/cacert.pem +0 -7815
@@ -0,0 +1,277 @@
|
|
1
|
+
module ActiveMerchant #:nodoc:
|
2
|
+
module Billing #:nodoc:
|
3
|
+
class OptimalPaymentGateway < Gateway
|
4
|
+
TEST_URL = 'https://webservices.test.optimalpayments.com/creditcardWS/CreditCardServlet/v1'
|
5
|
+
LIVE_URL = 'https://webservices.optimalpayments.com/creditcardWS/CreditCardServlet/v1'
|
6
|
+
|
7
|
+
# The countries the gateway supports merchants from as 2 digit ISO country codes
|
8
|
+
self.supported_countries = ['CA', 'US', 'GB']
|
9
|
+
|
10
|
+
# The card types supported by the payment gateway
|
11
|
+
self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :solo] # :switch?
|
12
|
+
|
13
|
+
# The homepage URL of the gateway
|
14
|
+
self.homepage_url = 'http://www.optimalpayments.com/'
|
15
|
+
|
16
|
+
# The name of the gateway
|
17
|
+
self.display_name = 'Optimal Payments'
|
18
|
+
|
19
|
+
def initialize(options = {})
|
20
|
+
#requires!(options, :login, :password)
|
21
|
+
@options = options
|
22
|
+
super
|
23
|
+
end
|
24
|
+
|
25
|
+
def authorize(money, card_or_auth, options = {})
|
26
|
+
parse_card_or_auth(card_or_auth, options)
|
27
|
+
commit("cc#{@stored_data}Authorize", money, options)
|
28
|
+
end
|
29
|
+
alias stored_authorize authorize # back-compat
|
30
|
+
|
31
|
+
def purchase(money, card_or_auth, options = {})
|
32
|
+
parse_card_or_auth(card_or_auth, options)
|
33
|
+
commit("cc#{@stored_data}Purchase", money, options)
|
34
|
+
end
|
35
|
+
alias stored_purchase purchase # back-compat
|
36
|
+
|
37
|
+
def refund(money, authorization, options = {})
|
38
|
+
options[:confirmationNumber] = authorization
|
39
|
+
commit('ccCredit', money, options)
|
40
|
+
end
|
41
|
+
|
42
|
+
def void(authorization, options = {})
|
43
|
+
options[:confirmationNumber] = authorization
|
44
|
+
commit('ccAuthorizeReversal', nil, options)
|
45
|
+
end
|
46
|
+
|
47
|
+
def capture(money, authorization, options = {})
|
48
|
+
options[:confirmationNumber] = authorization
|
49
|
+
commit('ccSettlement', money, options)
|
50
|
+
end
|
51
|
+
|
52
|
+
def test?
|
53
|
+
super || @options[:test]
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def parse_card_or_auth(card_or_auth, options)
|
59
|
+
if card_or_auth.respond_to?(:number)
|
60
|
+
@credit_card = card_or_auth
|
61
|
+
@stored_data = ""
|
62
|
+
else
|
63
|
+
options[:confirmationNumber] = card_or_auth
|
64
|
+
@stored_data = "StoredData"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def parse(body)
|
69
|
+
REXML::Document.new(body || '')
|
70
|
+
end
|
71
|
+
|
72
|
+
def commit(action, money, post)
|
73
|
+
post[:order_id] ||= 'order_id'
|
74
|
+
|
75
|
+
xml = case action
|
76
|
+
when 'ccAuthorize', 'ccPurchase', 'ccVerification'
|
77
|
+
cc_auth_request(money, post)
|
78
|
+
when 'ccCredit', 'ccSettlement'
|
79
|
+
cc_post_auth_request(money, post)
|
80
|
+
when 'ccStoredDataAuthorize', 'ccStoredDataPurchase'
|
81
|
+
cc_stored_data_request(money, post)
|
82
|
+
when 'ccAuthorizeReversal'
|
83
|
+
cc_auth_reversal_request(post)
|
84
|
+
#when 'ccCancelSettle', 'ccCancelCredit', 'ccCancelPayment'
|
85
|
+
# cc_cancel_request(money, post)
|
86
|
+
#when 'ccPayment'
|
87
|
+
# cc_payment_request(money, post)
|
88
|
+
#when 'ccAuthenticate'
|
89
|
+
# cc_authenticate_request(money, post)
|
90
|
+
else
|
91
|
+
raise 'Unknown Action'
|
92
|
+
end
|
93
|
+
txnRequest = URI.encode(xml)
|
94
|
+
response = parse(ssl_post(test? ? TEST_URL : LIVE_URL, "txnMode=#{action}&txnRequest=#{txnRequest}"))
|
95
|
+
|
96
|
+
Response.new(successful?(response), message_from(response), hash_from_xml(response),
|
97
|
+
:test => test?,
|
98
|
+
:authorization => authorization_from(response)
|
99
|
+
)
|
100
|
+
end
|
101
|
+
|
102
|
+
def successful?(response)
|
103
|
+
REXML::XPath.first(response, '//decision').text == 'ACCEPTED' rescue false
|
104
|
+
end
|
105
|
+
|
106
|
+
def message_from(response)
|
107
|
+
REXML::XPath.each(response, '//detail') do |detail|
|
108
|
+
if detail.is_a?(REXML::Element) && detail.elements['tag'].text == 'InternalResponseDescription'
|
109
|
+
return detail.elements['value'].text
|
110
|
+
end
|
111
|
+
end
|
112
|
+
nil
|
113
|
+
end
|
114
|
+
|
115
|
+
def authorization_from(response)
|
116
|
+
REXML::XPath.first(response, '//confirmationNumber').text rescue nil
|
117
|
+
end
|
118
|
+
|
119
|
+
def hash_from_xml(response)
|
120
|
+
hsh = {}
|
121
|
+
%w(confirmationNumber authCode
|
122
|
+
decision code description
|
123
|
+
actionCode avsResponse cvdResponse
|
124
|
+
txnTime duplicateFound
|
125
|
+
).each do |tag|
|
126
|
+
node = REXML::XPath.first(response, "//#{tag}")
|
127
|
+
hsh[tag] = node.text if node
|
128
|
+
end
|
129
|
+
REXML::XPath.each(response, '//detail') do |detail|
|
130
|
+
next unless detail.is_a?(REXML::Element)
|
131
|
+
tag = detail.elements['tag'].text
|
132
|
+
value = detail.elements['value'].text
|
133
|
+
hsh[tag] = value
|
134
|
+
end
|
135
|
+
hsh
|
136
|
+
end
|
137
|
+
|
138
|
+
def xml_document(root_tag)
|
139
|
+
xml = Builder::XmlMarkup.new :indent => 2
|
140
|
+
xml.tag!(root_tag, schema) do
|
141
|
+
yield xml
|
142
|
+
end
|
143
|
+
xml.target!
|
144
|
+
end
|
145
|
+
|
146
|
+
def cc_auth_request(money, opts)
|
147
|
+
xml_document('ccAuthRequestV1') do |xml|
|
148
|
+
build_merchant_account(xml, @options)
|
149
|
+
xml.merchantRefNum opts[:order_id]
|
150
|
+
xml.amount(money/100.0)
|
151
|
+
build_card(xml, opts)
|
152
|
+
build_billing_details(xml, opts)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def cc_auth_reversal_request(opts)
|
157
|
+
xml_document('ccAuthReversalRequestV1') do |xml|
|
158
|
+
build_merchant_account(xml, @options)
|
159
|
+
xml.confirmationNumber opts[:confirmationNumber]
|
160
|
+
xml.merchantRefNum opts[:order_id]
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def cc_post_auth_request(money, opts)
|
165
|
+
xml_document('ccPostAuthRequestV1') do |xml|
|
166
|
+
build_merchant_account(xml, @options)
|
167
|
+
xml.confirmationNumber opts[:confirmationNumber]
|
168
|
+
xml.merchantRefNum opts[:order_id]
|
169
|
+
xml.amount(money/100.0)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def cc_stored_data_request(money, opts)
|
174
|
+
xml_document('ccStoredDataRequestV1') do |xml|
|
175
|
+
build_merchant_account(xml, @options)
|
176
|
+
xml.merchantRefNum opts[:order_id]
|
177
|
+
xml.confirmationNumber opts[:confirmationNumber]
|
178
|
+
xml.amount(money/100.0)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
# untested
|
183
|
+
#
|
184
|
+
# def cc_cancel_request(opts)
|
185
|
+
# xml_document('ccCancelRequestV1') do |xml|
|
186
|
+
# build_merchant_account(xml, @options)
|
187
|
+
# xml.confirmationNumber opts[:confirmationNumber]
|
188
|
+
# end
|
189
|
+
# end
|
190
|
+
#
|
191
|
+
# def cc_payment_request(money, opts)
|
192
|
+
# xml_document('ccPaymentRequestV1') do |xml|
|
193
|
+
# build_merchant_account(xml, @options)
|
194
|
+
# xml.merchantRefNum opts[:order_id]
|
195
|
+
# xml.amount(money/100.0)
|
196
|
+
# build_card(xml, opts)
|
197
|
+
# build_billing_details(xml, opts)
|
198
|
+
# end
|
199
|
+
# end
|
200
|
+
#
|
201
|
+
# def cc_authenticate_request(opts)
|
202
|
+
# xml_document('ccAuthenticateRequestV1') do |xml|
|
203
|
+
# build_merchant_account(xml, @options)
|
204
|
+
# xml.confirmationNumber opts[:confirmationNumber]
|
205
|
+
# xml.paymentResponse 'myPaymentResponse'
|
206
|
+
# end
|
207
|
+
# end
|
208
|
+
|
209
|
+
def schema
|
210
|
+
{ 'xmlns' => 'http://www.optimalpayments.com/creditcard/xmlschema/v1',
|
211
|
+
'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
|
212
|
+
'xsi:schemaLocation' => 'http://www.optimalpayments.com/creditcard/xmlschema/v1'
|
213
|
+
}
|
214
|
+
end
|
215
|
+
|
216
|
+
def build_merchant_account(xml, opts)
|
217
|
+
xml.tag! 'merchantAccount' do
|
218
|
+
xml.tag! 'accountNum' , opts[:account]
|
219
|
+
xml.tag! 'storeID' , opts[:login]
|
220
|
+
xml.tag! 'storePwd' , opts[:password]
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def build_card(xml, opts)
|
225
|
+
xml.tag! 'card' do
|
226
|
+
xml.tag! 'cardNum' , @credit_card.number
|
227
|
+
xml.tag! 'cardExpiry' do
|
228
|
+
xml.tag! 'month' , @credit_card.month
|
229
|
+
xml.tag! 'year' , @credit_card.year
|
230
|
+
end
|
231
|
+
if type = card_type(@credit_card.type)
|
232
|
+
xml.tag! 'cardType' , type
|
233
|
+
end
|
234
|
+
if @credit_card.verification_value
|
235
|
+
xml.tag! 'cvdIndicator' , '1' # Value Provided
|
236
|
+
xml.tag! 'cvd' , @credit_card.verification_value
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
def build_billing_details(xml, opts)
|
242
|
+
xml.tag! 'billingDetails' do
|
243
|
+
addr = opts[:billing_address]
|
244
|
+
xml.tag! 'cardPayMethod', 'WEB'
|
245
|
+
if addr[:name]
|
246
|
+
xml.tag! 'firstName', CGI.escape(addr[:name].split(' ').first) # TODO: parse properly
|
247
|
+
xml.tag! 'lastName' , CGI.escape(addr[:name].split(' ').last )
|
248
|
+
end
|
249
|
+
xml.tag! 'street' , CGI.escape(addr[:address1]) if addr[:address1].present?
|
250
|
+
xml.tag! 'street2', CGI.escape(addr[:address2]) if addr[:address2].present?
|
251
|
+
xml.tag! 'city' , CGI.escape(addr[:city] ) if addr[:city].present?
|
252
|
+
if addr[:state].present?
|
253
|
+
state_tag = %w(US CA).include?(addr[:country]) ? 'state' : 'region'
|
254
|
+
xml.tag! state_tag, CGI.escape(addr[:state])
|
255
|
+
end
|
256
|
+
xml.tag! 'country', CGI.escape(addr[:country] ) if addr[:country].present?
|
257
|
+
xml.tag! 'zip' , CGI.escape(addr[:zip] ) # this one's actually required
|
258
|
+
xml.tag! 'phone' , CGI.escape(addr[:phone] ) if addr[:phone].present?
|
259
|
+
#xml.tag! 'email' , ''
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
def card_type(key)
|
264
|
+
{ 'visa' => 'VI',
|
265
|
+
'master' => 'MC',
|
266
|
+
'american_express'=> 'AM',
|
267
|
+
'discover' => 'DI',
|
268
|
+
'diners_club' => 'DC',
|
269
|
+
#'switch' => '',
|
270
|
+
'solo' => 'SO'
|
271
|
+
}[key]
|
272
|
+
end
|
273
|
+
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
@@ -78,10 +78,9 @@ module ActiveMerchant #:nodoc:
|
|
78
78
|
}
|
79
79
|
|
80
80
|
def initialize(options = {})
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
end
|
81
|
+
requires!(options, :merchant_id)
|
82
|
+
requires!(options, :login, :password) unless options[:ip_authentication]
|
83
|
+
@options = options
|
85
84
|
super
|
86
85
|
end
|
87
86
|
|
@@ -122,8 +121,8 @@ module ActiveMerchant #:nodoc:
|
|
122
121
|
end
|
123
122
|
|
124
123
|
# setting money to nil will perform a full void
|
125
|
-
def void(
|
126
|
-
order = build_void_request_xml(
|
124
|
+
def void(authorization, options = {})
|
125
|
+
order = build_void_request_xml(authorization, options)
|
127
126
|
commit(order)
|
128
127
|
end
|
129
128
|
|
@@ -167,6 +166,7 @@ module ActiveMerchant #:nodoc:
|
|
167
166
|
xml.tag! :CurrencyCode, currency_code(currency)
|
168
167
|
xml.tag! :CurrencyExponent, '2' # Will need updating to support currencies such as the Yen.
|
169
168
|
|
169
|
+
xml.tag! :CardSecValInd, 1 if creditcard.verification_value? && %w( visa discover ).include?(creditcard.brand)
|
170
170
|
xml.tag! :CardSecVal, creditcard.verification_value if creditcard.verification_value?
|
171
171
|
end
|
172
172
|
|
@@ -223,11 +223,11 @@ module ActiveMerchant #:nodoc:
|
|
223
223
|
end
|
224
224
|
|
225
225
|
def success?(response)
|
226
|
-
if response[:message_type] == "R"
|
226
|
+
if response[:message_type].nil? || response[:message_type] == "R"
|
227
227
|
response[:proc_status] == SUCCESS
|
228
228
|
else
|
229
229
|
response[:proc_status] == SUCCESS &&
|
230
|
-
|
230
|
+
response[:resp_code] == APPROVED
|
231
231
|
end
|
232
232
|
end
|
233
233
|
|
@@ -241,22 +241,18 @@ module ActiveMerchant #:nodoc:
|
|
241
241
|
|
242
242
|
def build_new_order_xml(action, money, parameters = {})
|
243
243
|
requires!(parameters, :order_id)
|
244
|
-
xml =
|
245
|
-
xml.instruct!(:xml, :version => '1.0', :encoding => 'UTF-8')
|
244
|
+
xml = xml_envelope
|
246
245
|
xml.tag! :Request do
|
247
246
|
xml.tag! :NewOrder do
|
248
|
-
xml
|
249
|
-
xml.tag! :
|
250
|
-
xml.tag! :IndustryType, "EC" # E-Commerce transaction
|
247
|
+
add_xml_credentials(xml)
|
248
|
+
xml.tag! :IndustryType, parameters[:industry_type] || "EC"
|
251
249
|
xml.tag! :MessageType, action
|
252
|
-
xml
|
253
|
-
xml.tag! :MerchantID, @options[:merchant_id]
|
254
|
-
xml.tag! :TerminalID, parameters[:terminal_id] || '001'
|
250
|
+
add_bin_merchant_and_terminal(xml, parameters)
|
255
251
|
|
256
252
|
yield xml if block_given?
|
257
253
|
|
258
254
|
xml.tag! :Comments, parameters[:comments] if parameters[:comments]
|
259
|
-
xml.tag! :OrderID, parameters[:order_id]
|
255
|
+
xml.tag! :OrderID, format_order_id(parameters[:order_id])
|
260
256
|
xml.tag! :Amount, amount(money)
|
261
257
|
|
262
258
|
# Append Transaction Reference Number at the end for Refund transactions
|
@@ -271,39 +267,29 @@ module ActiveMerchant #:nodoc:
|
|
271
267
|
|
272
268
|
def build_mark_for_capture_xml(money, authorization, parameters = {})
|
273
269
|
tx_ref_num, order_id = authorization.split(';')
|
274
|
-
xml =
|
275
|
-
xml.instruct!(:xml, :version => '1.0', :encoding => 'UTF-8')
|
270
|
+
xml = xml_envelope
|
276
271
|
xml.tag! :Request do
|
277
272
|
xml.tag! :MarkForCapture do
|
278
|
-
xml
|
279
|
-
xml.tag! :OrbitalConnectionPassword, @options[:password] unless ip_authentication?
|
273
|
+
add_xml_credentials(xml)
|
280
274
|
xml.tag! :OrderID, order_id
|
281
275
|
xml.tag! :Amount, amount(money)
|
282
|
-
xml
|
283
|
-
xml.tag! :MerchantID, @options[:merchant_id]
|
284
|
-
xml.tag! :TerminalID, parameters[:terminal_id] || '001'
|
276
|
+
add_bin_merchant_and_terminal(xml, parameters)
|
285
277
|
xml.tag! :TxRefNum, tx_ref_num
|
286
278
|
end
|
287
279
|
end
|
288
280
|
xml.target!
|
289
281
|
end
|
290
282
|
|
291
|
-
def build_void_request_xml(
|
292
|
-
requires!(parameters, :transaction_index)
|
283
|
+
def build_void_request_xml(authorization, parameters = {})
|
293
284
|
tx_ref_num, order_id = authorization.split(';')
|
294
|
-
xml =
|
295
|
-
xml.instruct!(:xml, :version => '1.0', :encoding => 'UTF-8')
|
285
|
+
xml = xml_envelope
|
296
286
|
xml.tag! :Request do
|
297
287
|
xml.tag! :Reversal do
|
298
|
-
xml
|
299
|
-
xml.tag! :OrbitalConnectionPassword, @options[:password] unless ip_authentication?
|
288
|
+
add_xml_credentials(xml)
|
300
289
|
xml.tag! :TxRefNum, tx_ref_num
|
301
290
|
xml.tag! :TxRefIdx, parameters[:transaction_index]
|
302
|
-
xml.tag! :AdjustedAmt, amount(money)
|
303
291
|
xml.tag! :OrderID, order_id
|
304
|
-
xml
|
305
|
-
xml.tag! :MerchantID, @options[:merchant_id]
|
306
|
-
xml.tag! :TerminalID, parameters[:terminal_id] || '001'
|
292
|
+
add_bin_merchant_and_terminal(xml, parameters)
|
307
293
|
end
|
308
294
|
end
|
309
295
|
xml.target!
|
@@ -316,6 +302,43 @@ module ActiveMerchant #:nodoc:
|
|
316
302
|
def expiry_date(credit_card)
|
317
303
|
"#{format(credit_card.month, :two_digits)}#{format(credit_card.year, :two_digits)}"
|
318
304
|
end
|
305
|
+
|
306
|
+
def bin
|
307
|
+
@options[:bin] || (salem_mid? ? '000001' : '000002')
|
308
|
+
end
|
309
|
+
|
310
|
+
def xml_envelope
|
311
|
+
xml = Builder::XmlMarkup.new(:indent => 2)
|
312
|
+
xml.instruct!(:xml, :version => '1.0', :encoding => 'UTF-8')
|
313
|
+
xml
|
314
|
+
end
|
315
|
+
|
316
|
+
def add_xml_credentials(xml)
|
317
|
+
xml.tag! :OrbitalConnectionUsername, @options[:login] unless ip_authentication?
|
318
|
+
xml.tag! :OrbitalConnectionPassword, @options[:password] unless ip_authentication?
|
319
|
+
end
|
320
|
+
|
321
|
+
def add_bin_merchant_and_terminal(xml, parameters)
|
322
|
+
xml.tag! :BIN, bin
|
323
|
+
xml.tag! :MerchantID, @options[:merchant_id]
|
324
|
+
xml.tag! :TerminalID, parameters[:terminal_id] || '001'
|
325
|
+
end
|
326
|
+
|
327
|
+
def salem_mid?
|
328
|
+
@options[:merchant_id].length == 6
|
329
|
+
end
|
330
|
+
|
331
|
+
# The valid characters include:
|
332
|
+
#
|
333
|
+
# 1. all letters and digits
|
334
|
+
# 2. - , $ @ & and a space character, though the space character cannot be the leading character
|
335
|
+
# 3. PINless Debit transactions can only use uppercase and lowercase alpha (A-Z, a-z) and numeric (0-9)
|
336
|
+
def format_order_id(order_id)
|
337
|
+
illegal_characters = /[^,$@\- \w]/
|
338
|
+
order_id = order_id.to_s.gsub(/\./, '-')
|
339
|
+
order_id.gsub!(illegal_characters, '')
|
340
|
+
order_id[0...22]
|
341
|
+
end
|
319
342
|
end
|
320
343
|
end
|
321
344
|
end
|
@@ -202,7 +202,7 @@ module ActiveMerchant #:nodoc:
|
|
202
202
|
|
203
203
|
# Return money to a card that was previously billed.
|
204
204
|
# _authorization_ should be the transaction id of the transaction we are returning.
|
205
|
-
def
|
205
|
+
def refund(money, authorization, options = {})
|
206
206
|
parameters = {
|
207
207
|
:transaction_amount => amount(money),
|
208
208
|
:transaction_id => authorization
|
@@ -211,6 +211,11 @@ module ActiveMerchant #:nodoc:
|
|
211
211
|
commit('CREDIT', parameters)
|
212
212
|
end
|
213
213
|
|
214
|
+
def credit(money, authorization, options = {})
|
215
|
+
deprecated CREDIT_DEPRECATION_MESSAGE
|
216
|
+
refund(money, authorization, options)
|
217
|
+
end
|
218
|
+
|
214
219
|
# Cancel a transaction that has been charged but has not yet made it
|
215
220
|
# through the batch process.
|
216
221
|
def void(authorization, options = {})
|
@@ -126,6 +126,7 @@ module ActiveMerchant #:nodoc:
|
|
126
126
|
xml.tag! 'EMail', options[:email] unless options[:email].blank?
|
127
127
|
xml.tag! 'Phone', address[:phone] unless address[:phone].blank?
|
128
128
|
xml.tag! 'CustCode', options[:customer] if !options[:customer].blank? && tag == 'BillTo'
|
129
|
+
xml.tag! 'PONum', options[:po_number] if !options[:po_number].blank? && tag == 'BillTo'
|
129
130
|
|
130
131
|
xml.tag! 'Address' do
|
131
132
|
xml.tag! 'Street', address[:address1] unless address[:address1].blank?
|
@@ -26,7 +26,7 @@ module ActiveMerchant #:nodoc:
|
|
26
26
|
{ 'name' => full_name,
|
27
27
|
'company' => nil,
|
28
28
|
'address1' => @params['street'],
|
29
|
-
'address2' => @params['shiptostreet2'],
|
29
|
+
'address2' => @params['shiptostreet2'] || @params['street2'],
|
30
30
|
'city' => @params['city'],
|
31
31
|
'state' => @params['state'],
|
32
32
|
'country' => @params['country'],
|
@@ -36,4 +36,4 @@ module ActiveMerchant #:nodoc:
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
end
|
39
|
-
end
|
39
|
+
end
|
@@ -94,6 +94,10 @@ module ActiveMerchant #:nodoc:
|
|
94
94
|
xml.tag! 'Description', options[:description] unless options[:description].blank?
|
95
95
|
xml.tag! 'Comment', options[:comment] unless options[:comment].blank?
|
96
96
|
xml.tag!('ExtData', 'Name'=> 'COMMENT2', 'Value'=> options[:comment2]) unless options[:comment2].blank?
|
97
|
+
xml.tag! 'TaxAmt', options[:taxamt] unless options[:taxamt].blank?
|
98
|
+
xml.tag! 'FreightAmt', options[:freightamt] unless options[:freightamt].blank?
|
99
|
+
xml.tag! 'DutyAmt', options[:dutyamt] unless options[:dutyamt].blank?
|
100
|
+
xml.tag! 'DiscountAmt', options[:discountamt] unless options[:discountamt].blank?
|
97
101
|
|
98
102
|
billing_address = options[:billing_address] || options[:address]
|
99
103
|
add_address(xml, 'BillTo', billing_address, options) if billing_address
|
@@ -122,6 +126,10 @@ module ActiveMerchant #:nodoc:
|
|
122
126
|
# Comment and Comment2 will show up in manager.paypal.com as Comment1 and Comment2
|
123
127
|
xml.tag! 'Comment', options[:comment] unless options[:comment].blank?
|
124
128
|
xml.tag!('ExtData', 'Name'=> 'COMMENT2', 'Value'=> options[:comment2]) unless options[:comment2].blank?
|
129
|
+
xml.tag! 'TaxAmt', options[:taxamt] unless options[:taxamt].blank?
|
130
|
+
xml.tag! 'FreightAmt', options[:freightamt] unless options[:freightamt].blank?
|
131
|
+
xml.tag! 'DutyAmt', options[:dutyamt] unless options[:dutyamt].blank?
|
132
|
+
xml.tag! 'DiscountAmt', options[:discountamt] unless options[:discountamt].blank?
|
125
133
|
|
126
134
|
billing_address = options[:billing_address] || options[:address]
|
127
135
|
add_address(xml, 'BillTo', billing_address, options) if billing_address
|
@@ -161,8 +169,8 @@ module ActiveMerchant #:nodoc:
|
|
161
169
|
end
|
162
170
|
|
163
171
|
def expdate(creditcard)
|
164
|
-
year = sprintf("%.4i", creditcard.year)
|
165
|
-
month = sprintf("%.2i", creditcard.month)
|
172
|
+
year = sprintf("%.4i", creditcard.year.to_s.sub(/^0+/, ''))
|
173
|
+
month = sprintf("%.2i", creditcard.month.to_s.sub(/^0+/, ''))
|
166
174
|
|
167
175
|
"#{year}#{month}"
|
168
176
|
end
|
@@ -8,7 +8,7 @@ module ActiveMerchant #:nodoc:
|
|
8
8
|
base.cattr_accessor :signature
|
9
9
|
end
|
10
10
|
|
11
|
-
API_VERSION = '
|
11
|
+
API_VERSION = '72'
|
12
12
|
|
13
13
|
URLS = {
|
14
14
|
:test => { :certificate => 'https://api.sandbox.paypal.com/2.0/',
|
@@ -63,10 +63,13 @@ module ActiveMerchant #:nodoc:
|
|
63
63
|
def initialize(options = {})
|
64
64
|
requires!(options, :login, :password)
|
65
65
|
|
66
|
+
headers = {'X-PP-AUTHORIZATION' => options.delete(:auth_signature), 'X-PAYPAL-MESSAGE-PROTOCOL' => 'SOAP11'} if options[:auth_signature]
|
66
67
|
@options = {
|
67
68
|
:pem => pem_file,
|
68
|
-
:signature => signature
|
69
|
+
:signature => signature,
|
70
|
+
:headers => headers || {}
|
69
71
|
}.update(options)
|
72
|
+
|
70
73
|
|
71
74
|
if @options[:pem].blank? && @options[:signature].blank?
|
72
75
|
raise ArgumentError, "An API Certificate or API Signature is required to make requests to PayPal"
|
@@ -280,7 +283,7 @@ module ActiveMerchant #:nodoc:
|
|
280
283
|
xml.instruct!
|
281
284
|
xml.tag! 'env:Envelope', ENVELOPE_NAMESPACES do
|
282
285
|
xml.tag! 'env:Header' do
|
283
|
-
add_credentials(xml)
|
286
|
+
add_credentials(xml) unless @options[:headers] && @options[:headers]['X-PP-AUTHORIZATION']
|
284
287
|
end
|
285
288
|
|
286
289
|
xml.tag! 'env:Body' do
|
@@ -310,8 +313,8 @@ module ActiveMerchant #:nodoc:
|
|
310
313
|
xml.tag! 'n2:CityName', address[:city]
|
311
314
|
xml.tag! 'n2:StateOrProvince', address[:state].blank? ? 'N/A' : address[:state]
|
312
315
|
xml.tag! 'n2:Country', address[:country]
|
313
|
-
xml.tag! 'n2:PostalCode', address[:zip]
|
314
316
|
xml.tag! 'n2:Phone', address[:phone]
|
317
|
+
xml.tag! 'n2:PostalCode', address[:zip]
|
315
318
|
end
|
316
319
|
end
|
317
320
|
|
@@ -320,7 +323,7 @@ module ActiveMerchant #:nodoc:
|
|
320
323
|
end
|
321
324
|
|
322
325
|
def commit(action, request)
|
323
|
-
response = parse(action, ssl_post(endpoint_url, build_request(request)))
|
326
|
+
response = parse(action, ssl_post(endpoint_url, build_request(request), @options[:headers]))
|
324
327
|
|
325
328
|
build_response(successful?(response), message_from(response), response,
|
326
329
|
:test => test?,
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/paypal/paypal_common_api'
|
2
|
+
require File.dirname(__FILE__) + '/paypal/paypal_express_response'
|
3
|
+
require File.dirname(__FILE__) + '/paypal_express_common'
|
4
|
+
|
5
|
+
module ActiveMerchant #:nodoc:
|
6
|
+
module Billing #:nodoc:
|
7
|
+
class PaypalDigitalGoodsGateway < PaypalExpressGateway
|
8
|
+
self.test_redirect_url = 'https://www.sandbox.paypal.com/incontext'
|
9
|
+
self.live_redirect_url = 'https://www.paypal.com/incontext'
|
10
|
+
|
11
|
+
self.supported_countries = %w(AU CA CN FI GB ID IN IT MY NO NZ PH PL SE SG TH VN)
|
12
|
+
self.homepage_url = 'https://www.x.com/community/ppx/xspaces/digital_goods'
|
13
|
+
self.display_name = 'PayPal Express Checkout for Digital Goods'
|
14
|
+
|
15
|
+
def redirect_url_for(token, options = {})
|
16
|
+
"#{redirect_url}?token=#{token}&useraction=commit"
|
17
|
+
end
|
18
|
+
|
19
|
+
# GATEWAY.setup_purchase(100,
|
20
|
+
# :ip => "127.0.0.1",
|
21
|
+
# :description => "Test Title",
|
22
|
+
# :return_url => "http://return.url",
|
23
|
+
# :cancel_return_url => "http://cancel.url",
|
24
|
+
# :items => [ { :name => "Charge",
|
25
|
+
# :number => "1",
|
26
|
+
# :quantity => "1",
|
27
|
+
# :amount => 100,
|
28
|
+
# :description => "Description",
|
29
|
+
# :category => "Digital" } ] )
|
30
|
+
def build_setup_request(action, money, options)
|
31
|
+
requires!(options, :items)
|
32
|
+
raise ArgumentError, "Must include at least 1 Item" unless options[:items].length > 0
|
33
|
+
options[:items].each do |item|
|
34
|
+
requires!(item, :name, :number, :quantity, :amount, :description, :category)
|
35
|
+
raise ArgumentError, "Each of the items must have the category 'Digital'" unless item[:category] == 'Digital'
|
36
|
+
end
|
37
|
+
|
38
|
+
super
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|