activemerchant 1.1.0 → 1.2.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 +226 -0
- data/CONTRIBUTERS +52 -0
- data/README +34 -24
- data/Rakefile +152 -0
- data/gem-public_cert.pem +20 -0
- data/init.rb +3 -0
- data/lib/active_merchant.rb +3 -1
- data/lib/active_merchant/billing/credit_card.rb +21 -17
- data/lib/active_merchant/billing/credit_card_methods.rb +17 -19
- data/lib/active_merchant/billing/gateway.rb +160 -44
- data/lib/active_merchant/billing/gateways.rb +2 -15
- data/lib/active_merchant/billing/gateways/authorize_net.rb +21 -21
- data/lib/active_merchant/billing/gateways/bogus.rb +6 -6
- data/lib/active_merchant/billing/gateways/brain_tree.rb +191 -0
- data/lib/active_merchant/billing/gateways/card_stream.rb +207 -0
- data/lib/active_merchant/billing/gateways/cyber_source.rb +402 -0
- data/lib/active_merchant/billing/gateways/data_cash.rb +41 -97
- data/lib/active_merchant/billing/gateways/efsnet.rb +256 -0
- data/lib/active_merchant/billing/gateways/eway.rb +77 -29
- data/lib/active_merchant/billing/gateways/exact.rb +230 -0
- data/lib/active_merchant/billing/gateways/linkpoint.rb +6 -33
- data/lib/active_merchant/billing/gateways/moneris.rb +155 -125
- data/lib/active_merchant/billing/gateways/net_registry.rb +257 -0
- data/lib/active_merchant/billing/gateways/pay_junction.rb +407 -0
- data/lib/active_merchant/billing/gateways/payflow.rb +163 -25
- data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +56 -38
- data/lib/active_merchant/billing/gateways/payflow/payflow_express_response.rb +10 -1
- data/lib/active_merchant/billing/gateways/payflow/payflow_response.rb +9 -0
- data/lib/active_merchant/billing/gateways/payflow_express.rb +36 -11
- data/lib/active_merchant/billing/gateways/payflow_express_uk.rb +6 -0
- data/lib/active_merchant/billing/gateways/payflow_uk.rb +7 -3
- data/lib/active_merchant/billing/gateways/payment_express.rb +261 -0
- data/lib/active_merchant/billing/gateways/paypal.rb +18 -4
- data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +31 -15
- data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +8 -0
- data/lib/active_merchant/billing/gateways/paypal_express.rb +33 -8
- data/lib/active_merchant/billing/gateways/plugnpay.rb +300 -0
- data/lib/active_merchant/billing/gateways/protx.rb +285 -0
- data/lib/active_merchant/billing/gateways/psigate.rb +13 -12
- data/lib/active_merchant/billing/gateways/psl_card.rb +297 -0
- data/lib/active_merchant/billing/gateways/quickpay.rb +197 -0
- data/lib/active_merchant/billing/gateways/realex.rb +212 -0
- data/lib/active_merchant/billing/gateways/secure_pay.rb +31 -0
- data/lib/active_merchant/billing/gateways/trans_first.rb +136 -0
- data/lib/active_merchant/billing/gateways/trust_commerce.rb +43 -20
- data/lib/active_merchant/billing/gateways/usa_epay.rb +6 -5
- data/lib/active_merchant/billing/gateways/verifi.rb +235 -0
- data/lib/active_merchant/billing/gateways/viaklix.rb +171 -0
- data/lib/active_merchant/billing/integrations/gestpay/helper.rb +0 -2
- data/lib/active_merchant/billing/integrations/helper.rb +8 -1
- data/lib/active_merchant/billing/integrations/nochex.rb +62 -1
- data/lib/active_merchant/billing/integrations/nochex/notification.rb +9 -16
- data/lib/active_merchant/billing/integrations/notification.rb +1 -1
- data/lib/active_merchant/billing/integrations/paypal/helper.rb +59 -46
- data/lib/active_merchant/billing/integrations/paypal/notification.rb +14 -47
- data/lib/active_merchant/lib/error.rb +4 -0
- data/lib/active_merchant/lib/post_data.rb +22 -0
- data/lib/active_merchant/lib/posts_data.rb +23 -5
- data/lib/active_merchant/lib/requires_parameters.rb +2 -2
- data/lib/active_merchant/lib/validateable.rb +1 -1
- data/lib/support/gateway_support.rb +45 -0
- data/lib/tasks/cia.rb +1 -1
- data/script/generate +14 -0
- data/script/generator/base.rb +45 -0
- data/script/generator/generator.rb +24 -0
- data/script/generator/generators/gateway/gateway_generator.rb +14 -0
- data/script/generator/generators/gateway/templates/gateway.rb +73 -0
- data/script/generator/generators/gateway/templates/gateway_test.rb +41 -0
- data/script/generator/generators/gateway/templates/remote_gateway_test.rb +56 -0
- data/script/generator/generators/integration/integration_generator.rb +25 -0
- data/script/generator/generators/integration/templates/helper.rb +34 -0
- data/script/generator/generators/integration/templates/helper_test.rb +54 -0
- data/script/generator/generators/integration/templates/integration.rb +18 -0
- data/script/generator/generators/integration/templates/module_test.rb +9 -0
- data/script/generator/generators/integration/templates/notification.rb +100 -0
- data/script/generator/generators/integration/templates/notification_test.rb +41 -0
- data/script/generator/manifest.rb +20 -0
- data/test/extra/binding_of_caller.rb +80 -0
- data/test/extra/breakpoint.rb +547 -0
- data/test/fixtures.yml +251 -0
- data/test/remote_tests/remote_authorize_net_test.rb +113 -0
- data/test/remote_tests/remote_brain_tree_test.rb +78 -0
- data/test/remote_tests/remote_card_stream_test.rb +160 -0
- data/test/remote_tests/remote_cyber_source_test.rb +130 -0
- data/test/remote_tests/remote_data_cash_test.rb +155 -0
- data/test/remote_tests/remote_efsnet_test.rb +93 -0
- data/test/remote_tests/remote_eway_test.rb +71 -0
- data/test/remote_tests/remote_exact_test.rb +59 -0
- data/test/remote_tests/remote_gestpay_integration_test.rb +37 -0
- data/test/remote_tests/remote_linkpoint_test.rb +144 -0
- data/test/remote_tests/remote_moneris_test.rb +110 -0
- data/test/remote_tests/remote_net_registry_test.rb +120 -0
- data/test/remote_tests/remote_pay_junction_test.rb +162 -0
- data/test/remote_tests/remote_payflow_express_test.rb +50 -0
- data/test/remote_tests/remote_payflow_test.rb +241 -0
- data/test/remote_tests/remote_payflow_uk_test.rb +172 -0
- data/test/remote_tests/remote_payment_express_test.rb +136 -0
- data/test/remote_tests/remote_paypal_express_test.rb +49 -0
- data/test/remote_tests/remote_paypal_integration_test.rb +14 -0
- data/test/remote_tests/remote_paypal_test.rb +163 -0
- data/test/remote_tests/remote_plugnpay_test.rb +70 -0
- data/test/remote_tests/remote_protx_test.rb +184 -0
- data/test/remote_tests/remote_psigate_test.rb +87 -0
- data/test/remote_tests/remote_psl_card_test.rb +105 -0
- data/test/remote_tests/remote_quickpay_test.rb +182 -0
- data/test/remote_tests/remote_realex_test.rb +227 -0
- data/test/remote_tests/remote_secure_pay_test.rb +36 -0
- data/test/remote_tests/remote_trans_first_test.rb +37 -0
- data/test/remote_tests/remote_trust_commerce_test.rb +136 -0
- data/test/remote_tests/remote_usa_epay_test.rb +57 -0
- data/test/remote_tests/remote_verifi_test.rb +107 -0
- data/test/remote_tests/remote_viaklix_test.rb +53 -0
- data/test/test_helper.rb +132 -0
- data/test/unit/base_test.rb +61 -0
- data/test/unit/country_code_test.rb +33 -0
- data/test/unit/country_test.rb +64 -0
- data/test/unit/credit_card_formatting_test.rb +24 -0
- data/test/unit/credit_card_methods_test.rb +37 -0
- data/test/unit/credit_card_test.rb +365 -0
- data/test/unit/gateways/authorize_net_test.rb +140 -0
- data/test/unit/gateways/bogus_test.rb +43 -0
- data/test/unit/gateways/brain_tree_test.rb +77 -0
- data/test/unit/gateways/card_stream_test.rb +37 -0
- data/test/unit/gateways/cyber_source_test.rb +151 -0
- data/test/unit/gateways/data_cash_test.rb +23 -0
- data/test/unit/gateways/efsnet_test.rb +70 -0
- data/test/unit/gateways/eway_test.rb +105 -0
- data/test/unit/gateways/exact_test.rb +118 -0
- data/test/unit/gateways/gateway_test.rb +24 -0
- data/test/unit/gateways/linkpoint_test.rb +165 -0
- data/test/unit/gateways/moneris_test.rb +167 -0
- data/test/unit/gateways/net_registry_test.rb +478 -0
- data/test/unit/gateways/pay_junction_test.rb +61 -0
- data/test/unit/gateways/payflow_express_test.rb +165 -0
- data/test/unit/gateways/payflow_express_uk_test.rb +14 -0
- data/test/unit/gateways/payflow_test.rb +230 -0
- data/test/unit/gateways/payflow_uk_test.rb +68 -0
- data/test/unit/gateways/payment_express_test.rb +215 -0
- data/test/unit/gateways/paypal_express_test.rb +222 -0
- data/test/unit/gateways/paypal_test.rb +241 -0
- data/test/unit/gateways/plugnpay_test.rb +79 -0
- data/test/unit/gateways/protx_test.rb +110 -0
- data/test/unit/gateways/psigate_test.rb +110 -0
- data/test/unit/gateways/psl_card_test.rb +51 -0
- data/test/unit/gateways/quickpay_test.rb +125 -0
- data/test/unit/gateways/realex_test.rb +150 -0
- data/test/unit/gateways/secure_pay_test.rb +78 -0
- data/test/unit/gateways/trans_first_test.rb +125 -0
- data/test/unit/gateways/trust_commerce_test.rb +57 -0
- data/test/unit/gateways/usa_epay_test.rb +117 -0
- data/test/unit/gateways/verifi_test.rb +91 -0
- data/test/unit/gateways/viaklix_test.rb +72 -0
- data/test/unit/integrations/action_view_helper_test.rb +54 -0
- data/test/unit/integrations/bogus_module_test.rb +16 -0
- data/test/unit/integrations/chronopay_module_test.rb +9 -0
- data/test/unit/integrations/gestpay_module_test.rb +10 -0
- data/test/unit/integrations/helpers/bogus_helper_test.rb +28 -0
- data/test/unit/integrations/helpers/chronopay_helper_test.rb +67 -0
- data/test/unit/integrations/helpers/gestpay_helper_test.rb +100 -0
- data/test/unit/integrations/helpers/nochex_helper_test.rb +53 -0
- data/test/unit/integrations/helpers/paypal_helper_test.rb +162 -0
- data/test/unit/integrations/helpers/two_checkout_helper_test.rb +92 -0
- data/test/unit/integrations/nochex_module_test.rb +9 -0
- data/test/unit/integrations/notifications/chronopay_notification_test.rb +66 -0
- data/test/unit/integrations/notifications/gestpay_notification_test.rb +60 -0
- data/test/unit/integrations/notifications/nochex_notification_test.rb +51 -0
- data/test/unit/integrations/notifications/notification_test.rb +41 -0
- data/test/unit/integrations/notifications/paypal_notification_test.rb +85 -0
- data/test/unit/integrations/notifications/two_checkout_notification_test.rb +55 -0
- data/test/unit/integrations/paypal_module_test.rb +24 -0
- data/test/unit/integrations/two_checkout_module_test.rb +9 -0
- data/test/unit/post_data_test.rb +55 -0
- data/test/unit/response_test.rb +14 -0
- data/test/unit/validateable_test.rb +56 -0
- metadata +160 -7
- metadata.gz.sig +0 -0
- data/lib/active_merchant/billing/gateways/payflow/f73e89fd.0 +0 -17
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
module ActiveMerchant #:nodoc:
|
|
2
|
+
module Billing #:nodoc:
|
|
3
|
+
class ProtxGateway < Gateway
|
|
4
|
+
cattr_accessor :simulate
|
|
5
|
+
self.simulate = false
|
|
6
|
+
|
|
7
|
+
TEST_URL = 'https://ukvpstest.protx.com/vspgateway/service'
|
|
8
|
+
LIVE_URL = 'https://ukvps.protx.com/vspgateway/service'
|
|
9
|
+
SIMULATOR_URL = 'https://ukvpstest.protx.com/VSPSimulator'
|
|
10
|
+
|
|
11
|
+
APPROVED = 'OK'
|
|
12
|
+
|
|
13
|
+
TRANSACTIONS = {
|
|
14
|
+
:purchase => 'PAYMENT',
|
|
15
|
+
:credit => 'REFUND',
|
|
16
|
+
:authorization => 'DEFERRED',
|
|
17
|
+
:capture => 'RELEASE',
|
|
18
|
+
:void => 'VOID'
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
CREDIT_CARDS = {
|
|
22
|
+
:visa => "VISA",
|
|
23
|
+
:master => "MC",
|
|
24
|
+
:delta => "DELTA",
|
|
25
|
+
:solo => "SOLO",
|
|
26
|
+
:maestro => "MAESTRO",
|
|
27
|
+
:american_express => "AMEX",
|
|
28
|
+
:electron => "UKE",
|
|
29
|
+
:diners_club => "DC",
|
|
30
|
+
:jcb => "JCB"
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
ELECTRON = /^(424519|42496[23]|450875|48440[6-8]|4844[1-5][1-5]|4917[3-5][0-9]|491880)\d{10}(\d{3})?$/
|
|
34
|
+
|
|
35
|
+
POST_HEADERS = { 'Content-Type' => 'application/x-www-form-urlencoded' }
|
|
36
|
+
|
|
37
|
+
attr_reader :url
|
|
38
|
+
attr_reader :response
|
|
39
|
+
attr_reader :options
|
|
40
|
+
|
|
41
|
+
self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :solo, :maestro, :diners_club]
|
|
42
|
+
self.supported_countries = ['GB']
|
|
43
|
+
self.default_currency = 'GBP'
|
|
44
|
+
|
|
45
|
+
self.homepage_url = 'http://www.protx.com'
|
|
46
|
+
self.display_name = 'Protx'
|
|
47
|
+
|
|
48
|
+
def initialize(options = {})
|
|
49
|
+
requires!(options, :login)
|
|
50
|
+
@options = options
|
|
51
|
+
super
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def test?
|
|
55
|
+
@options[:test] || Base.gateway_mode == :test
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def purchase(money, credit_card, options = {})
|
|
59
|
+
requires!(options, :order_id)
|
|
60
|
+
|
|
61
|
+
post = {}
|
|
62
|
+
|
|
63
|
+
add_amount(post, money, options)
|
|
64
|
+
add_invoice(post, options)
|
|
65
|
+
add_credit_card(post, credit_card)
|
|
66
|
+
add_address(post, options)
|
|
67
|
+
add_customer_data(post, options)
|
|
68
|
+
|
|
69
|
+
commit(:purchase, post)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def authorize(money, credit_card, options = {})
|
|
73
|
+
requires!(options, :order_id)
|
|
74
|
+
|
|
75
|
+
post = {}
|
|
76
|
+
|
|
77
|
+
add_amount(post, money, options)
|
|
78
|
+
add_invoice(post, options)
|
|
79
|
+
add_credit_card(post, credit_card)
|
|
80
|
+
add_address(post, options)
|
|
81
|
+
add_customer_data(post, options)
|
|
82
|
+
|
|
83
|
+
commit(:authorization, post)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Only supports capturing the original amount of the transaction
|
|
87
|
+
def capture(money, identification, options = {})
|
|
88
|
+
post = {}
|
|
89
|
+
|
|
90
|
+
add_reference(post, identification)
|
|
91
|
+
commit(:capture, post)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def void(identification, options = {})
|
|
95
|
+
post = {}
|
|
96
|
+
|
|
97
|
+
add_reference(post, identification)
|
|
98
|
+
commit(:void, post)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Crediting requires a new order_id to passed in, as well as a description
|
|
102
|
+
def credit(money, identification, options = {})
|
|
103
|
+
requires!(options, :order_id, :description)
|
|
104
|
+
|
|
105
|
+
post = {}
|
|
106
|
+
|
|
107
|
+
add_credit_reference(post, identification)
|
|
108
|
+
add_amount(post, money, options)
|
|
109
|
+
add_invoice(post, options)
|
|
110
|
+
|
|
111
|
+
commit(:credit, post)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
private
|
|
115
|
+
def add_reference(post, identification)
|
|
116
|
+
order_id, transaction_id, authorization, security_key = identification.split(';')
|
|
117
|
+
|
|
118
|
+
add_pair(post, :VendorTxCode, order_id)
|
|
119
|
+
add_pair(post, :VPSTxId, transaction_id)
|
|
120
|
+
add_pair(post, :TxAuthNo, authorization)
|
|
121
|
+
add_pair(post, :SecurityKey, security_key)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def add_credit_reference(post, identification)
|
|
125
|
+
order_id, transaction_id, authorization, security_key = identification.split(';')
|
|
126
|
+
|
|
127
|
+
add_pair(post, :RelatedVendorTxCode, order_id)
|
|
128
|
+
add_pair(post, :RelatedVPSTxId, transaction_id)
|
|
129
|
+
add_pair(post, :RelatedTxAuthNo, authorization)
|
|
130
|
+
add_pair(post, :RelatedSecurityKey, security_key)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def add_amount(post, money, options)
|
|
134
|
+
add_pair(post, :Amount, amount(money), :required => true)
|
|
135
|
+
add_pair(post, :Currency, options[:currency] || currency(money), :required => true)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def add_customer_data(post, options)
|
|
139
|
+
add_pair(post, :BillingEmail, options[:email])
|
|
140
|
+
add_pair(post, :ContactNumber, options[:phone])
|
|
141
|
+
add_pair(post, :ContactFax, options[:fax])
|
|
142
|
+
add_pair(post, :ClientIPAddress, options[:ip])
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def add_address(post, options)
|
|
146
|
+
address = options[:billing_address] || options[:address]
|
|
147
|
+
shipping_address = options[:shipping_address] || ''
|
|
148
|
+
|
|
149
|
+
return if address.blank?
|
|
150
|
+
|
|
151
|
+
billing = "#{address[:address1]}\n#{address[:address2]}\n#{address[:city]}\n#{address[:state]}"
|
|
152
|
+
|
|
153
|
+
add_pair(post, :BillingAddress, billing)
|
|
154
|
+
add_pair(post, :BillingPostcode, address[:zip])
|
|
155
|
+
|
|
156
|
+
return if shipping_address.blank?
|
|
157
|
+
|
|
158
|
+
shipping = "#{shipping_address[:address1]}\n#{shipping_address[:address2]}\n#{shipping_address[:city]}\n#{shipping_address[:state]}"
|
|
159
|
+
|
|
160
|
+
add_pair(post, :DeliveryAddress, shipping)
|
|
161
|
+
add_pair(post, :DeliveryPostcode, shipping_address[:zip])
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def add_invoice(post, options)
|
|
165
|
+
add_pair(post, :VendorTxCode, sanitize_order_id(options[:order_id]), :required => true)
|
|
166
|
+
add_pair(post, :Description, options[:description] || options[:order_id])
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def add_credit_card(post, credit_card)
|
|
170
|
+
add_pair(post, :CardHolder, credit_card.name, :required => true)
|
|
171
|
+
add_pair(post, :CardNumber, credit_card.number, :required => true)
|
|
172
|
+
|
|
173
|
+
add_pair(post, :ExpiryDate, format_expiry_date(credit_card), :required => true)
|
|
174
|
+
|
|
175
|
+
if requires_start_date_or_issue_number?(credit_card)
|
|
176
|
+
add_pair(post, :StartDate, format(credit_card.start_year, :four_digits))
|
|
177
|
+
|
|
178
|
+
add_pair(post, :IssueNumber, format_issue_number(credit_card))
|
|
179
|
+
end
|
|
180
|
+
add_pair(post, :CardType, map_card_type(credit_card))
|
|
181
|
+
|
|
182
|
+
add_pair(post, :CV2, credit_card.verification_value)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def sanitize_order_id(order_id)
|
|
186
|
+
order_id.to_s.gsub(/[^-a-zA-Z0-9._]/, '')
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def map_card_type(credit_card)
|
|
190
|
+
raise ArgumentError, "The credit card type must be provided" if credit_card.type.blank?
|
|
191
|
+
|
|
192
|
+
card_type = credit_card.type.to_sym
|
|
193
|
+
|
|
194
|
+
# Check if it is an electron card
|
|
195
|
+
if card_type == :visa && credit_card.number =~ ELECTRON
|
|
196
|
+
CREDIT_CARDS[:electron]
|
|
197
|
+
else
|
|
198
|
+
CREDIT_CARDS[card_type]
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# MMYY format
|
|
203
|
+
def format_expiry_date(credit_card)
|
|
204
|
+
year = sprintf("%.4i", credit_card.year)
|
|
205
|
+
month = sprintf("%.2i", credit_card.month)
|
|
206
|
+
|
|
207
|
+
"#{month}#{year[-2..-1]}"
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def format_issue_number(credit_card)
|
|
211
|
+
credit_card.type.to_s == 'solo' ? format(credit_card.issue_number, :two_digits) : credit_card.issue_number
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def commit(action, parameters)
|
|
215
|
+
if result = test_result_from_cc_number(parameters[:CardNumber])
|
|
216
|
+
return result
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
data = ssl_post(build_endpoint_url(action), post_data(action, parameters), POST_HEADERS)
|
|
220
|
+
|
|
221
|
+
@response = parse(data)
|
|
222
|
+
|
|
223
|
+
success = @response["Status"] == APPROVED
|
|
224
|
+
message = message_from(@response)
|
|
225
|
+
|
|
226
|
+
authorization = [ parameters[:VendorTxCode],
|
|
227
|
+
@response["VPSTxId"],
|
|
228
|
+
@response["TxAuthNo"],
|
|
229
|
+
@response["SecurityKey"] ].compact.join(";")
|
|
230
|
+
|
|
231
|
+
Response.new(success, message, @response,
|
|
232
|
+
:test => test?,
|
|
233
|
+
:authorization => authorization
|
|
234
|
+
)
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def build_endpoint_url(action)
|
|
238
|
+
simulate ? build_simulator_url(action) : build_url(action)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def build_url(action)
|
|
242
|
+
endpoint = [ :purchase, :authorization ].include?(action) ? "vspdirect-register" : TRANSACTIONS[action].downcase
|
|
243
|
+
"#{test? ? TEST_URL : LIVE_URL}/#{endpoint}.vsp"
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def build_simulator_url(action)
|
|
247
|
+
endpoint = [ :purchase, :authorization ].include?(action) ? "VSPDirectGateway.asp" : "VSPServerGateway.asp?Service=Vendor#{TRANSACTIONS[action].capitalize}Tx"
|
|
248
|
+
"#{SIMULATOR_URL}/#{endpoint}"
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def message_from(results)
|
|
252
|
+
if response["Status"] == APPROVED
|
|
253
|
+
return 'Success'
|
|
254
|
+
else
|
|
255
|
+
return 'Unspecified error' if response["StatusDetail"].blank?
|
|
256
|
+
return response["StatusDetail"]
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
def post_data(action, parameters = {})
|
|
261
|
+
parameters.update(
|
|
262
|
+
:Vendor => @options[:login],
|
|
263
|
+
:TxType => TRANSACTIONS[action],
|
|
264
|
+
:VPSProtocol => "2.22"
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
parameters.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&")
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
# Protx returns data in the following format
|
|
271
|
+
# Key1=value1
|
|
272
|
+
# Key2=value2
|
|
273
|
+
def parse(body)
|
|
274
|
+
result = {}
|
|
275
|
+
body.to_a.collect {|v| c=v.split('='); result[c[0]] = c[1].chomp if !c[1].blank? }
|
|
276
|
+
result
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def add_pair(post, key, value, options = {})
|
|
280
|
+
post[key] = value if !value.blank? || options[:required]
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
|
|
@@ -7,19 +7,19 @@
|
|
|
7
7
|
#
|
|
8
8
|
# Usage for a PreAuth (authorize) is as follows:
|
|
9
9
|
#
|
|
10
|
-
# twenty =
|
|
11
|
-
# gateway = PsigateGateway.new(
|
|
10
|
+
# twenty = 2000
|
|
11
|
+
# gateway = PsigateGateway.new(
|
|
12
12
|
# :store_id => 'teststore',
|
|
13
|
-
# :password => 'psigate1234'
|
|
14
|
-
#
|
|
13
|
+
# :password => 'psigate1234'
|
|
14
|
+
# )
|
|
15
15
|
#
|
|
16
|
-
# creditcard = CreditCard.new(
|
|
16
|
+
# creditcard = CreditCard.new(
|
|
17
17
|
# :number => '4242424242424242',
|
|
18
18
|
# :month => 8,
|
|
19
19
|
# :year => 2006,
|
|
20
20
|
# :first_name => 'Longbob',
|
|
21
21
|
# :last_name => 'Longsen'
|
|
22
|
-
#
|
|
22
|
+
# )
|
|
23
23
|
# response = @gateway.authorize(twenty, creditcard, {:order_id => 1234,
|
|
24
24
|
# :billing_address => {
|
|
25
25
|
# :address1 => '123 fairweather Lane',
|
|
@@ -46,6 +46,12 @@ module ActiveMerchant #:nodoc:
|
|
|
46
46
|
TEST_URL = 'https://dev.psigate.com:7989/Messenger/XMLMessenger'
|
|
47
47
|
LIVE_URL = 'https://secure.psigate.com:7934/Messenger/XMLMessenger'
|
|
48
48
|
|
|
49
|
+
self.supported_cardtypes = [:visa, :master, :american_express]
|
|
50
|
+
self.supported_countries = ['CA']
|
|
51
|
+
self.homepage_url = 'http://www.psigate.com/'
|
|
52
|
+
self.display_name = 'Psigate'
|
|
53
|
+
|
|
54
|
+
|
|
49
55
|
def initialize(options = {})
|
|
50
56
|
requires!(options, :login, :password)
|
|
51
57
|
|
|
@@ -85,12 +91,7 @@ module ActiveMerchant #:nodoc:
|
|
|
85
91
|
options.update({ :CardAction => "3", :order_id => authorization })
|
|
86
92
|
commit(money, nil, options)
|
|
87
93
|
end
|
|
88
|
-
|
|
89
|
-
# We support visa and master card
|
|
90
|
-
def self.supported_cardtypes
|
|
91
|
-
[:visa, :master]
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
+
|
|
94
95
|
private
|
|
95
96
|
|
|
96
97
|
def commit(money, creditcard, options = {})
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
# Author:: MoneySpyder, http://moneyspyder.co.uk
|
|
2
|
+
|
|
3
|
+
module ActiveMerchant
|
|
4
|
+
module Billing
|
|
5
|
+
#
|
|
6
|
+
# ActiveMerchant PSL Card Gateway
|
|
7
|
+
#
|
|
8
|
+
# Notes:
|
|
9
|
+
# -To be able to use the capture function, the IP address of the machine must be
|
|
10
|
+
# registered with PSL
|
|
11
|
+
# -ESALE_KEYED should only be used in situations where the cardholder perceives the
|
|
12
|
+
# transaction to be Internet-based, such as purchasing from a web site/on-line store.
|
|
13
|
+
# If the Internet is used purely for the transport of information from the merchant
|
|
14
|
+
# directly to the gateway then the appropriate cardholder present or not present message
|
|
15
|
+
# type should be used rather than the ‘E’ equivalent.
|
|
16
|
+
# -The CV2 / AVS policies are set up with the account settings when signing up for an account
|
|
17
|
+
#
|
|
18
|
+
#
|
|
19
|
+
class PslCardGateway < Gateway
|
|
20
|
+
self.money_format = :cents
|
|
21
|
+
self.default_currency = 'GBP'
|
|
22
|
+
|
|
23
|
+
self.supported_countries = ['GB']
|
|
24
|
+
# Visa Credit, Visa Debit, Mastercard, Maestro, Solo, Electron,
|
|
25
|
+
# American Express, Diners Club, JCB, International Maestro,
|
|
26
|
+
# Style, Clydesdale Financial Services, Other
|
|
27
|
+
|
|
28
|
+
self.supported_cardtypes = [ :visa, :master, :american_express, :diners_club, :jcb, :switch, :solo, :maestro ]
|
|
29
|
+
self.homepage_url = 'http://www.paymentsolutionsltd.com/'
|
|
30
|
+
self.display_name = 'PSL Payment Solutions'
|
|
31
|
+
|
|
32
|
+
# Default ISO 3166 country code (GB)
|
|
33
|
+
cattr_accessor :location
|
|
34
|
+
self.location = 826
|
|
35
|
+
|
|
36
|
+
# PslCard server URL - The url is the same whether testing or live - use
|
|
37
|
+
# the test account when testing...
|
|
38
|
+
URL = 'https://pslcard3.paymentsolutionsltd.com/secure/transact.asp?'
|
|
39
|
+
|
|
40
|
+
attr_reader :url
|
|
41
|
+
attr_reader :response
|
|
42
|
+
attr_reader :options
|
|
43
|
+
|
|
44
|
+
# eCommerce sale transaction, details keyed by merchant or cardholder
|
|
45
|
+
MESSAGE_TYPE = 'ESALE_KEYED'
|
|
46
|
+
|
|
47
|
+
# The type of response that we want to get from PSL, options are HTML, XML or REDIRECT
|
|
48
|
+
RESPONSE_ACTION = 'HTML'
|
|
49
|
+
|
|
50
|
+
# Currency Codes
|
|
51
|
+
CURRENCY_CODES = {
|
|
52
|
+
'AUD' => 036,
|
|
53
|
+
'GBP' => 826,
|
|
54
|
+
'USD' => 840
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
#The terminal used - only for swipe transactions, so hard coded to 32 for online
|
|
58
|
+
EMV_TERMINAL_TYPE = 32
|
|
59
|
+
|
|
60
|
+
#Different Dispatch types
|
|
61
|
+
DISPATCH_LATER = 'LATER'
|
|
62
|
+
DISPATCH_NOW = 'NOW'
|
|
63
|
+
|
|
64
|
+
# Return codes
|
|
65
|
+
APPROVED = '00'
|
|
66
|
+
|
|
67
|
+
#Nominal amount to authorize for a 'dispatch later' type
|
|
68
|
+
#The nominal amount is held straight away, when the goods are ready
|
|
69
|
+
#to be dispatched, PSL is informed and the full amount is the
|
|
70
|
+
#taken.
|
|
71
|
+
NOMINAL_AMOUNT = 101
|
|
72
|
+
|
|
73
|
+
# Create a new PslCardGateway
|
|
74
|
+
#
|
|
75
|
+
# The gateway requires that a valid :login be passed in the options hash
|
|
76
|
+
#
|
|
77
|
+
# Paramaters:
|
|
78
|
+
# -options:
|
|
79
|
+
# :login - the PslCard account login (required)
|
|
80
|
+
def initialize(options = {})
|
|
81
|
+
requires!(options, :login)
|
|
82
|
+
|
|
83
|
+
@options = options
|
|
84
|
+
super
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Purchase the item straight away
|
|
88
|
+
#
|
|
89
|
+
# Parameters:
|
|
90
|
+
# -money: Money object for the total to be charged
|
|
91
|
+
# -authorization: the PSL cross reference from the previous authorization
|
|
92
|
+
# -options:
|
|
93
|
+
#
|
|
94
|
+
# Returns:
|
|
95
|
+
# -ActiveRecord::Billing::Response object
|
|
96
|
+
#
|
|
97
|
+
def purchase(money, credit_card, options = {})
|
|
98
|
+
post = {}
|
|
99
|
+
|
|
100
|
+
add_amount(post, money, DISPATCH_NOW, options)
|
|
101
|
+
add_credit_card(post, credit_card)
|
|
102
|
+
add_address(post, options)
|
|
103
|
+
add_invoice(post, options)
|
|
104
|
+
add_purchase_details(post)
|
|
105
|
+
|
|
106
|
+
commit(post)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Authorize the transaction
|
|
110
|
+
#
|
|
111
|
+
# Reserves the funds on the customer's credit card, but does not
|
|
112
|
+
# charge the card.
|
|
113
|
+
#
|
|
114
|
+
# This implementation does not authorize the full amount, rather it checks that the full amount
|
|
115
|
+
# is available and only 'reserves' the nominal amount (currently a pound and a penny)
|
|
116
|
+
#
|
|
117
|
+
# Parameters:
|
|
118
|
+
# -money: Money object for the total to be charged
|
|
119
|
+
# -authorization: the PSL cross reference from the previous authorization
|
|
120
|
+
# -options:
|
|
121
|
+
#
|
|
122
|
+
# Returns:
|
|
123
|
+
# -ActiveRecord::Billing::Response object
|
|
124
|
+
#
|
|
125
|
+
def authorize(money, credit_card, options = {})
|
|
126
|
+
post = {}
|
|
127
|
+
|
|
128
|
+
add_amount(post, money, DISPATCH_LATER, options)
|
|
129
|
+
add_credit_card(post, credit_card)
|
|
130
|
+
add_address(post, options)
|
|
131
|
+
add_invoice(post, options)
|
|
132
|
+
add_purchase_details(post)
|
|
133
|
+
|
|
134
|
+
commit(post)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Post an authorization.
|
|
138
|
+
#
|
|
139
|
+
# Captures the funds from an authorized transaction.
|
|
140
|
+
#
|
|
141
|
+
# Parameters:
|
|
142
|
+
# -money: Money object for the total to be charged
|
|
143
|
+
# -authorization: The PSL Cross Reference
|
|
144
|
+
# -options:
|
|
145
|
+
#
|
|
146
|
+
# Returns:
|
|
147
|
+
# -ActiveRecord::Billing::Response object
|
|
148
|
+
#
|
|
149
|
+
def capture(money, authorization, options = {})
|
|
150
|
+
post = {}
|
|
151
|
+
|
|
152
|
+
add_amount(post, money, DISPATCH_NOW, options)
|
|
153
|
+
add_reference(post, authorization)
|
|
154
|
+
|
|
155
|
+
commit(post)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
private
|
|
159
|
+
|
|
160
|
+
def add_credit_card(post, credit_card)
|
|
161
|
+
post[:QAName] = credit_card.name
|
|
162
|
+
post[:CardNumber] = credit_card.number
|
|
163
|
+
post[:EMVTerminalType] = EMV_TERMINAL_TYPE
|
|
164
|
+
post[:ExpMonth] = credit_card.month
|
|
165
|
+
post[:ExpYear] = credit_card.year
|
|
166
|
+
post[:IssueNumber] = credit_card.issue_number unless credit_card.issue_number.blank?
|
|
167
|
+
post[:StartMonth] = credit_card.start_month unless credit_card.start_month.blank?
|
|
168
|
+
post[:StartYear] = credit_card.start_year unless credit_card.start_year.blank?
|
|
169
|
+
|
|
170
|
+
# CV2 check
|
|
171
|
+
post[:AVSCV2Check] = credit_card.verification_value? ? 'YES' : 'NO'
|
|
172
|
+
post[:CV2] = credit_card.verification_value if credit_card.verification_value?
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def add_address(post, options)
|
|
176
|
+
address = options[:billing_address] || options[:address]
|
|
177
|
+
return if address.nil?
|
|
178
|
+
|
|
179
|
+
post[:QAAddress] = [:address1, :address2, :city, :state].collect{|a| address[a]}.reject{|a| a.blank?}.join(' ')
|
|
180
|
+
post[:QAPostcode] = address[:zip]
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def add_invoice(post, options)
|
|
184
|
+
post[:MerchantName] = options[:merchant] || 'Merchant Name' # May use this as the order_id field
|
|
185
|
+
post[:OrderID] = options[:order_id] unless options[:order_id].blank?
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def add_reference(post, authorization)
|
|
189
|
+
post[:CrossReference] = authorization
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def add_amount(post, money, dispatch_type, options)
|
|
193
|
+
post[:CurrencyCode] = currency_code(options[:currency] || currency(money))
|
|
194
|
+
|
|
195
|
+
if dispatch_type == DISPATCH_LATER
|
|
196
|
+
post[:amount] = amount(NOMINAL_AMOUNT)
|
|
197
|
+
post[:DispatchLaterAmount] = amount(money)
|
|
198
|
+
else
|
|
199
|
+
post[:amount] = amount(money)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
post[:Dispatch] = dispatch_type
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def add_purchase_details(post)
|
|
206
|
+
post[:EchoAmount] = 'YES'
|
|
207
|
+
post[:SCBI] = 'YES' # Return information about the transaction
|
|
208
|
+
post[:MessageType] = MESSAGE_TYPE
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Get the currency code for the passed money object
|
|
212
|
+
#
|
|
213
|
+
# The money class stores the currency as an ISO 4217:2001 Alphanumeric,
|
|
214
|
+
# however PSL requires the ISO 4217:2001 Numeric code.
|
|
215
|
+
#
|
|
216
|
+
# Parameters:
|
|
217
|
+
# -money: Money object with the amount and currency
|
|
218
|
+
#
|
|
219
|
+
# Returns:
|
|
220
|
+
# -the ISO 4217:2001 Numberic currency code
|
|
221
|
+
#
|
|
222
|
+
def currency_code(currency)
|
|
223
|
+
CURRENCY_CODES[currency]
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# Parse the PSL response and create a Response object
|
|
227
|
+
#
|
|
228
|
+
# Parameters:
|
|
229
|
+
# -body: The response string returned from PSL, Formatted:
|
|
230
|
+
# Key=value&key=value...
|
|
231
|
+
#
|
|
232
|
+
# Returns:
|
|
233
|
+
# -a hash with all of the values returned in the PSL response
|
|
234
|
+
#
|
|
235
|
+
def parse(body)
|
|
236
|
+
|
|
237
|
+
fields = {}
|
|
238
|
+
for line in body.split('&')
|
|
239
|
+
key, value = *line.scan( %r{^(\w+)\=(.*)$} ).flatten
|
|
240
|
+
fields[key] = CGI.unescape(value)
|
|
241
|
+
end
|
|
242
|
+
fields.symbolize_keys
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
# Send the passed data to PSL for processing
|
|
246
|
+
#
|
|
247
|
+
# Parameters:
|
|
248
|
+
# -request: The data that is to be sent to PSL
|
|
249
|
+
#
|
|
250
|
+
# Returns:
|
|
251
|
+
# - ActiveMerchant::Billing::Response object
|
|
252
|
+
#
|
|
253
|
+
def commit(request)
|
|
254
|
+
if result = test_result_from_cc_number(request[:CardNumber])
|
|
255
|
+
return result
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
result = ssl_post(URL, post_data(request))
|
|
259
|
+
|
|
260
|
+
@response = parse(result)
|
|
261
|
+
|
|
262
|
+
success = @response[:ResponseCode] == APPROVED
|
|
263
|
+
message = @response[:Message]
|
|
264
|
+
|
|
265
|
+
Response.new(success, message, @response,
|
|
266
|
+
:test => test?,
|
|
267
|
+
:authorization => @response[:CrossReference]
|
|
268
|
+
)
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# Put the passed data into a format that can be submitted to PSL
|
|
272
|
+
# Key=Value&Key=Value...
|
|
273
|
+
#
|
|
274
|
+
# Any ampersands and equal signs are removed from the data being posted
|
|
275
|
+
# as PSL puts them back into the response string which then cannot be parsed.
|
|
276
|
+
# This is after escaping before sending the request to PSL - this is a work
|
|
277
|
+
# around for the time being
|
|
278
|
+
#
|
|
279
|
+
# Parameters:
|
|
280
|
+
# -post: Hash of all the data to be sent
|
|
281
|
+
#
|
|
282
|
+
# Returns:
|
|
283
|
+
# -String: the data to be sent
|
|
284
|
+
#
|
|
285
|
+
def post_data(post)
|
|
286
|
+
post[:CountryCode] = self.location
|
|
287
|
+
post[:MerchantID] = @options[:login]
|
|
288
|
+
post[:ValidityID] = @options[:password]
|
|
289
|
+
post[:ResponseAction] = RESPONSE_ACTION
|
|
290
|
+
|
|
291
|
+
post.collect { |key, value|
|
|
292
|
+
"#{key}=#{CGI.escape(value.to_s.tr('&=', ' '))}"
|
|
293
|
+
}.join("&")
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
end
|