activemerchant 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (178) hide show
  1. data.tar.gz.sig +0 -0
  2. data/CHANGELOG +226 -0
  3. data/CONTRIBUTERS +52 -0
  4. data/README +34 -24
  5. data/Rakefile +152 -0
  6. data/gem-public_cert.pem +20 -0
  7. data/init.rb +3 -0
  8. data/lib/active_merchant.rb +3 -1
  9. data/lib/active_merchant/billing/credit_card.rb +21 -17
  10. data/lib/active_merchant/billing/credit_card_methods.rb +17 -19
  11. data/lib/active_merchant/billing/gateway.rb +160 -44
  12. data/lib/active_merchant/billing/gateways.rb +2 -15
  13. data/lib/active_merchant/billing/gateways/authorize_net.rb +21 -21
  14. data/lib/active_merchant/billing/gateways/bogus.rb +6 -6
  15. data/lib/active_merchant/billing/gateways/brain_tree.rb +191 -0
  16. data/lib/active_merchant/billing/gateways/card_stream.rb +207 -0
  17. data/lib/active_merchant/billing/gateways/cyber_source.rb +402 -0
  18. data/lib/active_merchant/billing/gateways/data_cash.rb +41 -97
  19. data/lib/active_merchant/billing/gateways/efsnet.rb +256 -0
  20. data/lib/active_merchant/billing/gateways/eway.rb +77 -29
  21. data/lib/active_merchant/billing/gateways/exact.rb +230 -0
  22. data/lib/active_merchant/billing/gateways/linkpoint.rb +6 -33
  23. data/lib/active_merchant/billing/gateways/moneris.rb +155 -125
  24. data/lib/active_merchant/billing/gateways/net_registry.rb +257 -0
  25. data/lib/active_merchant/billing/gateways/pay_junction.rb +407 -0
  26. data/lib/active_merchant/billing/gateways/payflow.rb +163 -25
  27. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +56 -38
  28. data/lib/active_merchant/billing/gateways/payflow/payflow_express_response.rb +10 -1
  29. data/lib/active_merchant/billing/gateways/payflow/payflow_response.rb +9 -0
  30. data/lib/active_merchant/billing/gateways/payflow_express.rb +36 -11
  31. data/lib/active_merchant/billing/gateways/payflow_express_uk.rb +6 -0
  32. data/lib/active_merchant/billing/gateways/payflow_uk.rb +7 -3
  33. data/lib/active_merchant/billing/gateways/payment_express.rb +261 -0
  34. data/lib/active_merchant/billing/gateways/paypal.rb +18 -4
  35. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +31 -15
  36. data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +8 -0
  37. data/lib/active_merchant/billing/gateways/paypal_express.rb +33 -8
  38. data/lib/active_merchant/billing/gateways/plugnpay.rb +300 -0
  39. data/lib/active_merchant/billing/gateways/protx.rb +285 -0
  40. data/lib/active_merchant/billing/gateways/psigate.rb +13 -12
  41. data/lib/active_merchant/billing/gateways/psl_card.rb +297 -0
  42. data/lib/active_merchant/billing/gateways/quickpay.rb +197 -0
  43. data/lib/active_merchant/billing/gateways/realex.rb +212 -0
  44. data/lib/active_merchant/billing/gateways/secure_pay.rb +31 -0
  45. data/lib/active_merchant/billing/gateways/trans_first.rb +136 -0
  46. data/lib/active_merchant/billing/gateways/trust_commerce.rb +43 -20
  47. data/lib/active_merchant/billing/gateways/usa_epay.rb +6 -5
  48. data/lib/active_merchant/billing/gateways/verifi.rb +235 -0
  49. data/lib/active_merchant/billing/gateways/viaklix.rb +171 -0
  50. data/lib/active_merchant/billing/integrations/gestpay/helper.rb +0 -2
  51. data/lib/active_merchant/billing/integrations/helper.rb +8 -1
  52. data/lib/active_merchant/billing/integrations/nochex.rb +62 -1
  53. data/lib/active_merchant/billing/integrations/nochex/notification.rb +9 -16
  54. data/lib/active_merchant/billing/integrations/notification.rb +1 -1
  55. data/lib/active_merchant/billing/integrations/paypal/helper.rb +59 -46
  56. data/lib/active_merchant/billing/integrations/paypal/notification.rb +14 -47
  57. data/lib/active_merchant/lib/error.rb +4 -0
  58. data/lib/active_merchant/lib/post_data.rb +22 -0
  59. data/lib/active_merchant/lib/posts_data.rb +23 -5
  60. data/lib/active_merchant/lib/requires_parameters.rb +2 -2
  61. data/lib/active_merchant/lib/validateable.rb +1 -1
  62. data/lib/support/gateway_support.rb +45 -0
  63. data/lib/tasks/cia.rb +1 -1
  64. data/script/generate +14 -0
  65. data/script/generator/base.rb +45 -0
  66. data/script/generator/generator.rb +24 -0
  67. data/script/generator/generators/gateway/gateway_generator.rb +14 -0
  68. data/script/generator/generators/gateway/templates/gateway.rb +73 -0
  69. data/script/generator/generators/gateway/templates/gateway_test.rb +41 -0
  70. data/script/generator/generators/gateway/templates/remote_gateway_test.rb +56 -0
  71. data/script/generator/generators/integration/integration_generator.rb +25 -0
  72. data/script/generator/generators/integration/templates/helper.rb +34 -0
  73. data/script/generator/generators/integration/templates/helper_test.rb +54 -0
  74. data/script/generator/generators/integration/templates/integration.rb +18 -0
  75. data/script/generator/generators/integration/templates/module_test.rb +9 -0
  76. data/script/generator/generators/integration/templates/notification.rb +100 -0
  77. data/script/generator/generators/integration/templates/notification_test.rb +41 -0
  78. data/script/generator/manifest.rb +20 -0
  79. data/test/extra/binding_of_caller.rb +80 -0
  80. data/test/extra/breakpoint.rb +547 -0
  81. data/test/fixtures.yml +251 -0
  82. data/test/remote_tests/remote_authorize_net_test.rb +113 -0
  83. data/test/remote_tests/remote_brain_tree_test.rb +78 -0
  84. data/test/remote_tests/remote_card_stream_test.rb +160 -0
  85. data/test/remote_tests/remote_cyber_source_test.rb +130 -0
  86. data/test/remote_tests/remote_data_cash_test.rb +155 -0
  87. data/test/remote_tests/remote_efsnet_test.rb +93 -0
  88. data/test/remote_tests/remote_eway_test.rb +71 -0
  89. data/test/remote_tests/remote_exact_test.rb +59 -0
  90. data/test/remote_tests/remote_gestpay_integration_test.rb +37 -0
  91. data/test/remote_tests/remote_linkpoint_test.rb +144 -0
  92. data/test/remote_tests/remote_moneris_test.rb +110 -0
  93. data/test/remote_tests/remote_net_registry_test.rb +120 -0
  94. data/test/remote_tests/remote_pay_junction_test.rb +162 -0
  95. data/test/remote_tests/remote_payflow_express_test.rb +50 -0
  96. data/test/remote_tests/remote_payflow_test.rb +241 -0
  97. data/test/remote_tests/remote_payflow_uk_test.rb +172 -0
  98. data/test/remote_tests/remote_payment_express_test.rb +136 -0
  99. data/test/remote_tests/remote_paypal_express_test.rb +49 -0
  100. data/test/remote_tests/remote_paypal_integration_test.rb +14 -0
  101. data/test/remote_tests/remote_paypal_test.rb +163 -0
  102. data/test/remote_tests/remote_plugnpay_test.rb +70 -0
  103. data/test/remote_tests/remote_protx_test.rb +184 -0
  104. data/test/remote_tests/remote_psigate_test.rb +87 -0
  105. data/test/remote_tests/remote_psl_card_test.rb +105 -0
  106. data/test/remote_tests/remote_quickpay_test.rb +182 -0
  107. data/test/remote_tests/remote_realex_test.rb +227 -0
  108. data/test/remote_tests/remote_secure_pay_test.rb +36 -0
  109. data/test/remote_tests/remote_trans_first_test.rb +37 -0
  110. data/test/remote_tests/remote_trust_commerce_test.rb +136 -0
  111. data/test/remote_tests/remote_usa_epay_test.rb +57 -0
  112. data/test/remote_tests/remote_verifi_test.rb +107 -0
  113. data/test/remote_tests/remote_viaklix_test.rb +53 -0
  114. data/test/test_helper.rb +132 -0
  115. data/test/unit/base_test.rb +61 -0
  116. data/test/unit/country_code_test.rb +33 -0
  117. data/test/unit/country_test.rb +64 -0
  118. data/test/unit/credit_card_formatting_test.rb +24 -0
  119. data/test/unit/credit_card_methods_test.rb +37 -0
  120. data/test/unit/credit_card_test.rb +365 -0
  121. data/test/unit/gateways/authorize_net_test.rb +140 -0
  122. data/test/unit/gateways/bogus_test.rb +43 -0
  123. data/test/unit/gateways/brain_tree_test.rb +77 -0
  124. data/test/unit/gateways/card_stream_test.rb +37 -0
  125. data/test/unit/gateways/cyber_source_test.rb +151 -0
  126. data/test/unit/gateways/data_cash_test.rb +23 -0
  127. data/test/unit/gateways/efsnet_test.rb +70 -0
  128. data/test/unit/gateways/eway_test.rb +105 -0
  129. data/test/unit/gateways/exact_test.rb +118 -0
  130. data/test/unit/gateways/gateway_test.rb +24 -0
  131. data/test/unit/gateways/linkpoint_test.rb +165 -0
  132. data/test/unit/gateways/moneris_test.rb +167 -0
  133. data/test/unit/gateways/net_registry_test.rb +478 -0
  134. data/test/unit/gateways/pay_junction_test.rb +61 -0
  135. data/test/unit/gateways/payflow_express_test.rb +165 -0
  136. data/test/unit/gateways/payflow_express_uk_test.rb +14 -0
  137. data/test/unit/gateways/payflow_test.rb +230 -0
  138. data/test/unit/gateways/payflow_uk_test.rb +68 -0
  139. data/test/unit/gateways/payment_express_test.rb +215 -0
  140. data/test/unit/gateways/paypal_express_test.rb +222 -0
  141. data/test/unit/gateways/paypal_test.rb +241 -0
  142. data/test/unit/gateways/plugnpay_test.rb +79 -0
  143. data/test/unit/gateways/protx_test.rb +110 -0
  144. data/test/unit/gateways/psigate_test.rb +110 -0
  145. data/test/unit/gateways/psl_card_test.rb +51 -0
  146. data/test/unit/gateways/quickpay_test.rb +125 -0
  147. data/test/unit/gateways/realex_test.rb +150 -0
  148. data/test/unit/gateways/secure_pay_test.rb +78 -0
  149. data/test/unit/gateways/trans_first_test.rb +125 -0
  150. data/test/unit/gateways/trust_commerce_test.rb +57 -0
  151. data/test/unit/gateways/usa_epay_test.rb +117 -0
  152. data/test/unit/gateways/verifi_test.rb +91 -0
  153. data/test/unit/gateways/viaklix_test.rb +72 -0
  154. data/test/unit/integrations/action_view_helper_test.rb +54 -0
  155. data/test/unit/integrations/bogus_module_test.rb +16 -0
  156. data/test/unit/integrations/chronopay_module_test.rb +9 -0
  157. data/test/unit/integrations/gestpay_module_test.rb +10 -0
  158. data/test/unit/integrations/helpers/bogus_helper_test.rb +28 -0
  159. data/test/unit/integrations/helpers/chronopay_helper_test.rb +67 -0
  160. data/test/unit/integrations/helpers/gestpay_helper_test.rb +100 -0
  161. data/test/unit/integrations/helpers/nochex_helper_test.rb +53 -0
  162. data/test/unit/integrations/helpers/paypal_helper_test.rb +162 -0
  163. data/test/unit/integrations/helpers/two_checkout_helper_test.rb +92 -0
  164. data/test/unit/integrations/nochex_module_test.rb +9 -0
  165. data/test/unit/integrations/notifications/chronopay_notification_test.rb +66 -0
  166. data/test/unit/integrations/notifications/gestpay_notification_test.rb +60 -0
  167. data/test/unit/integrations/notifications/nochex_notification_test.rb +51 -0
  168. data/test/unit/integrations/notifications/notification_test.rb +41 -0
  169. data/test/unit/integrations/notifications/paypal_notification_test.rb +85 -0
  170. data/test/unit/integrations/notifications/two_checkout_notification_test.rb +55 -0
  171. data/test/unit/integrations/paypal_module_test.rb +24 -0
  172. data/test/unit/integrations/two_checkout_module_test.rb +9 -0
  173. data/test/unit/post_data_test.rb +55 -0
  174. data/test/unit/response_test.rb +14 -0
  175. data/test/unit/validateable_test.rb +56 -0
  176. metadata +160 -7
  177. metadata.gz.sig +0 -0
  178. data/lib/active_merchant/billing/gateways/payflow/f73e89fd.0 +0 -17
@@ -1,19 +1,21 @@
1
- require 'digest/sha1'
2
-
3
1
  module ActiveMerchant #:nodoc:
4
2
  module Billing #:nodoc:
5
3
  module PayflowCommonAPI
6
4
  def self.included(base)
7
- base.class_inheritable_accessor :default_currency
8
5
  base.default_currency = 'USD'
9
6
 
10
7
  # The certification_id is required by PayPal to make direct HTTPS posts to their servers.
11
- # You can obtain a certification id by emailing: payflowintegrator@paypal.com
8
+ # The certification_id has been deprecated by PayPal. It will soon be removed and you can simply
9
+ # use the certification_id that has been configured here, or generate your own
12
10
  base.class_inheritable_accessor :certification_id
11
+ base.certification_id = '55d64dfec398cbbe66c1bf843cbad9'
12
+
13
13
  base.class_inheritable_accessor :partner
14
14
 
15
15
  # Set the default partner to PayPal
16
16
  base.partner = 'PayPal'
17
+
18
+ base.supported_countries = ['US', 'CA', 'SG', 'AU']
17
19
  end
18
20
 
19
21
  XMLNS = 'http://www.paypal.com/XMLPay'
@@ -30,6 +32,14 @@ module ActiveMerchant #:nodoc:
30
32
  :switch => 'Switch',
31
33
  :solo => 'Solo'
32
34
  }
35
+
36
+ TRANSACTIONS = {
37
+ :purchase => "Sale",
38
+ :authorization => "Authorization",
39
+ :capture => "Capture",
40
+ :void => "Void",
41
+ :credit => "Credit"
42
+ }
33
43
 
34
44
  def initialize(options = {})
35
45
  requires!(options, :login, :password)
@@ -46,33 +56,37 @@ module ActiveMerchant #:nodoc:
46
56
  end
47
57
 
48
58
  def capture(money, authorization, options = {})
49
- request = build_void_or_capture_request('Capture', authorization)
59
+ request = build_reference_request(:capture, money, authorization, options)
50
60
  commit(request)
51
61
  end
52
62
 
53
63
  def void(authorization, options = {})
54
- request = build_void_or_capture_request('Void', authorization)
64
+ request = build_reference_request(:void, nil, authorization, options)
55
65
  commit(request)
56
66
  end
57
-
67
+
58
68
  private
59
- def build_request(body)
69
+ def build_request(body, request_type = nil)
60
70
  xml = Builder::XmlMarkup.new :indent => 2
61
71
  xml.instruct!
62
72
  xml.tag! 'XMLPayRequest', 'Timeout' => 30, 'version' => "2.1", "xmlns" => XMLNS do
63
73
  xml.tag! 'RequestData' do
64
74
  xml.tag! 'Vendor', @options[:login]
65
75
  xml.tag! 'Partner', @options[:partner]
66
- xml.tag! 'Transactions' do
67
- xml.tag! 'Transaction' do
68
- xml.tag! 'Verbosity', 'MEDIUM'
69
- xml << body
76
+ if request_type == :recurring
77
+ xml << body
78
+ else
79
+ xml.tag! 'Transactions' do
80
+ xml.tag! 'Transaction' do
81
+ xml.tag! 'Verbosity', 'MEDIUM'
82
+ xml << body
83
+ end
70
84
  end
71
85
  end
72
86
  end
73
87
  xml.tag! 'RequestAuth' do
74
88
  xml.tag! 'UserPass' do
75
- xml.tag! 'User', @options[:login]
89
+ xml.tag! 'User', !@options[:user].blank? ? @options[:user] : @options[:login]
76
90
  xml.tag! 'Password', @options[:password]
77
91
  end
78
92
  end
@@ -80,21 +94,20 @@ module ActiveMerchant #:nodoc:
80
94
  xml.target!
81
95
  end
82
96
 
83
- def build_void_or_capture_request(action, authorization)
97
+ def build_reference_request(action, money, authorization, options)
84
98
  xml = Builder::XmlMarkup.new :indent => 2
85
- xml.tag! action do
99
+ xml.tag! TRANSACTIONS[action] do
86
100
  xml.tag! 'PNRef', authorization
101
+
102
+ unless money.nil?
103
+ xml.tag! 'Invoice' do
104
+ xml.tag! 'TotalAmt', amount(money), 'Currency' => options[:currency] || currency(money)
105
+ end
106
+ end
87
107
  end
108
+
88
109
  xml.target!
89
110
  end
90
-
91
- def add_paypal_details(xml, options)
92
- xml.tag! 'PayPal' do
93
- xml.tag! 'EMail', options[:email]
94
- xml.tag! 'ReturnURL', options[:return_url]
95
- xml.tag! 'CancelURL', options[:cancel_return_url]
96
- end
97
- end
98
111
 
99
112
  def add_address(xml, tag, address, options)
100
113
  return if address.nil?
@@ -105,7 +118,7 @@ module ActiveMerchant #:nodoc:
105
118
  xml.tag! 'Address' do
106
119
  xml.tag! 'Street', address[:address1] unless address[:address1].blank?
107
120
  xml.tag! 'City', address[:city] unless address[:city].blank?
108
- xml.tag! 'State', address[:state] unless address[:state].blank?
121
+ xml.tag! 'State', address[:state].blank? ? "N/A" : address[:state]
109
122
  xml.tag! 'Country', address[:country] unless address[:country].blank?
110
123
  xml.tag! 'Zip', address[:zip] unless address[:zip].blank?
111
124
  end
@@ -131,19 +144,13 @@ module ActiveMerchant #:nodoc:
131
144
  def parse_element(response, node)
132
145
  if node.has_elements?
133
146
  node.elements.each{|e| parse_element(response, e) }
147
+ elsif node.name == 'ExtData'
148
+ response[node.attributes['Name'].underscore.to_sym] = node.attributes['Value']
134
149
  else
135
150
  response[node.name.underscore.to_sym] = node.text
136
151
  end
137
152
  end
138
153
 
139
- def generate_request_token
140
- Digest::SHA1.hexdigest(rand.to_s).slice(0,32)
141
- end
142
-
143
- def currency(money)
144
- money.respond_to?(:currency) ? money.currency : self.default_currency
145
- end
146
-
147
154
  def build_headers(content_length)
148
155
  {
149
156
  "Content-Type" => "text/xml",
@@ -152,16 +159,21 @@ module ActiveMerchant #:nodoc:
152
159
  "X-VPS-VIT-Client-Certification-Id" => @options[:certification_id].to_s,
153
160
  "X-VPS-VIT-Integration-Product" => "ActiveMerchant",
154
161
  "X-VPS-VIT-Runtime-Version" => RUBY_VERSION,
155
- "X-VPS-Request-ID" => generate_request_token
162
+ "X-VPS-Request-ID" => generate_unique_id
156
163
  }
157
164
  end
158
165
 
159
- def commit(request_body)
160
- request = build_request(request_body)
161
- headers = build_headers(request_body.size)
162
-
166
+ def commit(request_body, request_type = nil)
167
+ request = build_request(request_body, request_type)
168
+ headers = build_headers(request.size)
169
+
170
+ if result = test_result_from_cc_number(parse_credit_card_number(request))
171
+ return result
172
+ end
173
+
163
174
  url = test? ? TEST_URL : LIVE_URL
164
175
  data = ssl_post(url, request, headers)
176
+
165
177
  @response = parse(data)
166
178
 
167
179
  success = @response[:result] == "0"
@@ -169,9 +181,15 @@ module ActiveMerchant #:nodoc:
169
181
 
170
182
  build_response(success, message, @response,
171
183
  :test => test?,
172
- :authorization => @response[:pn_ref]
184
+ :authorization => @response[:pn_ref] || @response[:rp_ref]
173
185
  )
174
186
  end
187
+
188
+ def parse_credit_card_number(request)
189
+ xml = REXML::Document.new(request)
190
+ card_number = REXML::XPath.first(xml, '//Tender/Card/CardNum')
191
+ card_number && card_number.text
192
+ end
175
193
  end
176
194
  end
177
195
  end
@@ -5,6 +5,10 @@ module ActiveMerchant #:nodoc:
5
5
  @params['e_mail']
6
6
  end
7
7
 
8
+ def full_name
9
+ "#{@params['name']} #{@params['lastname']}"
10
+ end
11
+
8
12
  def token
9
13
  @params['token']
10
14
  end
@@ -13,8 +17,13 @@ module ActiveMerchant #:nodoc:
13
17
  @params['payer_id']
14
18
  end
15
19
 
20
+ # Really the shipping country, but it is all the information provided
21
+ def payer_country
22
+ address['country']
23
+ end
24
+
16
25
  def address
17
- { 'name' => @params['name'],
26
+ { 'name' => full_name,
18
27
  'company' => nil,
19
28
  'address1' => @params['street'],
20
29
  'address2' => nil,
@@ -0,0 +1,9 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class PayflowResponse < Response
4
+ def profile_id
5
+ @params['profile_id']
6
+ end
7
+ end
8
+ end
9
+ end
@@ -3,12 +3,15 @@ require File.dirname(__FILE__) + '/payflow/payflow_express_response'
3
3
 
4
4
  module ActiveMerchant #:nodoc:
5
5
  module Billing #:nodoc:
6
- class PayflowExpressGateway < Gateway
6
+ class PayflowExpressGateway < Gateway
7
7
  include PayflowCommonAPI
8
8
 
9
9
  LIVE_REDIRECT_URL = 'https://www.paypal.com/cgibin/webscr?cmd=_express-checkout&token='
10
10
  TEST_REDIRECT_URL = 'https://test-expresscheckout.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token='
11
11
 
12
+ self.homepage_url = 'https://www.paypal.com/cgi-bin/webscr?cmd=xpt/merchant/ExpressCheckoutIntro-outside'
13
+ self.display_name = 'PayPal Express Checkout'
14
+
12
15
  def redirect_url
13
16
  test? ? TEST_REDIRECT_URL : LIVE_REDIRECT_URL
14
17
  end
@@ -27,8 +30,13 @@ module ActiveMerchant #:nodoc:
27
30
  requires!(options, :token, :payer_id)
28
31
  request = build_sale_or_authorization_request('Sale', money, options)
29
32
  commit(request)
30
- end
31
-
33
+ end
34
+
35
+ def credit(money, identification, options = {})
36
+ request = build_reference_request(:credit, money, identification, options)
37
+ commit(request)
38
+ end
39
+
32
40
  def setup_authorization(money, options = {})
33
41
  requires!(options, :return_url, :cancel_return_url)
34
42
 
@@ -46,7 +54,7 @@ module ActiveMerchant #:nodoc:
46
54
  def details_for(token)
47
55
  request = build_get_express_details_request(token)
48
56
  commit(request)
49
- end
57
+ end
50
58
 
51
59
  private
52
60
  def build_get_express_details_request(token)
@@ -81,7 +89,7 @@ module ActiveMerchant #:nodoc:
81
89
  add_address(xml, 'BillTo', billing_address, options)
82
90
  add_address(xml, 'ShipTo', shipping_address, options)
83
91
 
84
- xml.tag! 'TotalAmt', amount(money), 'Currency' => currency(money)
92
+ xml.tag! 'TotalAmt', amount(money), 'Currency' => options[:currency] || currency(money)
85
93
  end
86
94
 
87
95
  xml.tag! 'Tender' do
@@ -99,14 +107,10 @@ module ActiveMerchant #:nodoc:
99
107
  xml.tag! action do
100
108
  xml.tag! 'PayData' do
101
109
  xml.tag! 'Invoice' do
102
- xml.tag! 'TotalAmt', amount(money), 'Currency' => currency(money)
110
+ xml.tag! 'TotalAmt', amount(money), 'Currency' => options[:currency] || currency(money)
103
111
  end
104
112
  xml.tag! 'Tender' do
105
- xml.tag! 'PayPal' do
106
- xml.tag! 'Token', options[:token]
107
- xml.tag! 'PayerID', options[:payer_id]
108
- xml.tag! 'NotifyURL', options[:notify_url]
109
- end
113
+ add_paypal_details xml, options
110
114
  end
111
115
  end
112
116
  end
@@ -114,6 +118,27 @@ module ActiveMerchant #:nodoc:
114
118
  xml.target!
115
119
  end
116
120
 
121
+ def add_paypal_details(xml, options)
122
+ xml.tag! 'PayPal' do
123
+ xml.tag! 'EMail', options[:email] unless options[:email].blank?
124
+ xml.tag! 'ReturnURL', options[:return_url] unless options[:return_url].blank?
125
+ xml.tag! 'CancelURL', options[:cancel_return_url] unless options[:cancel_return_url].blank?
126
+ xml.tag! 'NotifyURL', options[:notify_url] unless options[:notify_url].blank?
127
+ xml.tag! 'PayerID', options[:payer_id] unless options[:payer_id].blank?
128
+ xml.tag! 'Token', options[:token] unless options[:token].blank?
129
+ xml.tag! 'NoShipping', options[:no_shipping] ? '1' : '0'
130
+ xml.tag! 'AddressOverride', options[:address_override] ? '1' : '0'
131
+ xml.tag! 'ButtonSource', application_id.to_s.slice(0,32) unless application_id.blank?
132
+
133
+ # Customization of the payment page
134
+ xml.tag! 'PageStyle', options[:page_style] unless options[:page_style].blank?
135
+ xml.tag! 'HeaderImage', options[:header_image] unless options[:header_image].blank?
136
+ xml.tag! 'HeaderBackColor', options[:header_background_color] unless options[:header_background_color].blank?
137
+ xml.tag! 'HeaderBorderColor', options[:header_border_color] unless options[:header_border_color].blank?
138
+ xml.tag! 'PayflowColor', options[:background_color] unless options[:background_color].blank?
139
+ end
140
+ end
141
+
117
142
  def build_response(success, message, response, options = {})
118
143
  PayflowExpressResponse.new(success, message, response, options)
119
144
  end
@@ -1,8 +1,14 @@
1
+ require File.dirname(__FILE__) + '/payflow_express'
2
+
1
3
  module ActiveMerchant #:nodoc:
2
4
  module Billing #:nodoc:
3
5
  class PayflowExpressUkGateway < PayflowExpressGateway
4
6
  self.default_currency = 'GBP'
5
7
  self.partner = 'PayPalUk'
8
+
9
+ self.supported_countries = ['GB']
10
+ self.homepage_url = 'https://www.paypal.com/uk/cgi-bin/webscr?cmd=_additional-payment-overview-outside'
11
+ self.display_name = 'PayPal Express Checkout (UK)'
6
12
  end
7
13
  end
8
14
  end
@@ -1,3 +1,6 @@
1
+ require File.dirname(__FILE__) + '/payflow'
2
+ require File.dirname(__FILE__) + '/payflow_express_uk'
3
+
1
4
  module ActiveMerchant #:nodoc:
2
5
  module Billing #:nodoc:
3
6
  class PayflowUkGateway < PayflowGateway
@@ -8,9 +11,10 @@ module ActiveMerchant #:nodoc:
8
11
  @express ||= PayflowExpressUkGateway.new(@options)
9
12
  end
10
13
 
11
- def self.supported_cardtypes
12
- [:visa, :master, :american_express, :discover, :solo, :switch]
13
- end
14
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover, :solo, :switch]
15
+ self.supported_countries = ['GB']
16
+ self.homepage_url = 'https://www.paypal.com/uk/cgi-bin/webscr?cmd=_wp-pro-overview-outside'
17
+ self.display_name = 'PayPal Website Payments Pro (UK)'
14
18
  end
15
19
  end
16
20
  end
@@ -0,0 +1,261 @@
1
+ require 'rexml/document'
2
+
3
+ module ActiveMerchant
4
+ module Billing
5
+
6
+ # In NZ DPS supports ANZ, Westpac, National Bank, ASB and BNZ.
7
+ # In Australia DPS supports ANZ, NAB, Westpac, CBA, St George and Bank of South Australia.
8
+ # The Maybank in Malaysia is supported and the Citibank for Singapore.
9
+ class PaymentExpressGateway < Gateway
10
+ attr_reader :url
11
+ attr_reader :response
12
+ attr_reader :options
13
+
14
+ self.default_currency = 'NZD'
15
+ # PS supports all major credit cards; Visa, Mastercard, Amex, Diners, BankCard & JCB.
16
+ # Various white label cards can be accepted as well; Farmers, AirNZCard and Elders etc.
17
+ # Please note that not all acquirers and Eftpos networks can support some of these card types.
18
+ # VISA, Mastercard, Diners Club and Farmers cards are supported
19
+ #
20
+ # However, regular accounts with DPS only support VISA and Mastercard
21
+ self.supported_cardtypes = [ :visa, :master, :american_express, :diners_club, :jcb ]
22
+
23
+ self.supported_countries = [ 'AU', 'MY', 'NZ', 'SG', 'ZA', 'GB', 'US' ]
24
+
25
+ self.homepage_url = 'http://www.paymentexpress.com/'
26
+ self.display_name = 'PaymentExpress'
27
+
28
+ PAYMENT_URL = 'https://www.paymentexpress.com/pxpost.aspx'
29
+
30
+ APPROVED = '1'
31
+
32
+ TRANSACTIONS = {
33
+ :purchase => 'Purchase',
34
+ :credit => 'Refund',
35
+ :authorization => 'Auth',
36
+ :capture => 'Complete',
37
+ :validate => 'Validate'
38
+ }
39
+
40
+ POST_HEADERS = { "Content-Type" => "application/x-www-form-urlencoded" }
41
+
42
+ # We require the DPS gateway username and password when the object is created.
43
+ def initialize(options = {})
44
+ # A DPS username and password must exist
45
+ requires!(options, :login, :password)
46
+ # Make the options an instance variable
47
+ @options = options
48
+ super
49
+ end
50
+
51
+ # Funds are transferred immediately.
52
+ def purchase(money, credit_card_or_billing_token, options = {})
53
+
54
+ credit_card = credit_card_or_billing_token if credit_card_or_billing_token.respond_to?(:number)
55
+
56
+ if credit_card
57
+ if result = test_result_from_cc_number(credit_card.number)
58
+ return result
59
+ end
60
+ options[:credit_card] = credit_card
61
+ else
62
+ options[:token] = credit_card_or_billing_token
63
+ end
64
+
65
+ request = build_purchase_or_authorization_request(money, options)
66
+ commit(:purchase, request)
67
+ end
68
+
69
+ # NOTE: Perhaps in options we allow a transaction note to be inserted
70
+ # Verifies that funds are available for the requested card and amount and reserves the specified amount.
71
+ # See: http://www.paymentexpress.com/technical_resources/ecommerce_nonhosted/pxpost.html#Authcomplete
72
+ def authorize(money, credit_card, options = {})
73
+ if result = test_result_from_cc_number(credit_card.number)
74
+ return result
75
+ end
76
+
77
+ options[:credit_card] = credit_card
78
+
79
+ request = build_purchase_or_authorization_request(money, options)
80
+ commit(:authorization, request)
81
+ end
82
+
83
+ # Transfer pre-authorized funds immediately
84
+ # See: http://www.paymentexpress.com/technical_resources/ecommerce_nonhosted/pxpost.html#Authcomplete
85
+ def capture(money, identification, options = {})
86
+ request = build_capture_or_credit_request(money, identification, options)
87
+ commit(:capture, request)
88
+ end
89
+
90
+ # Refund funds to the card holder
91
+ def credit(money, identification, options = {})
92
+ requires!(options, :description)
93
+
94
+ request = build_capture_or_credit_request(money, identification, options)
95
+ commit(:credit, request)
96
+ end
97
+
98
+ # token based billing
99
+
100
+ # initiates a "Validate" transcation to store card data on payment express servers
101
+ # returns a "token" that can be used to rebill this card
102
+ # see: http://www.paymentexpress.com/technical_resources/ecommerce_nonhosted/pxpost.html#Tokenbilling
103
+ # PaymentExpress does not support unstoring a stored card.
104
+ def store(credit_card, options = {})
105
+ request = build_token_request(credit_card, options)
106
+ commit(:validate, request)
107
+ end
108
+
109
+ private
110
+
111
+ def build_purchase_or_authorization_request(money, options)
112
+ result = new_transaction
113
+
114
+ returning result do |r|
115
+ add_credit_card(r, options[:credit_card]) if options[:credit_card]
116
+ add_billing_token(r, options[:token]) if options[:token]
117
+
118
+ add_amount(r, money, options)
119
+ add_invoice(r, options)
120
+ add_address_verification_data(r, options)
121
+ end
122
+ end
123
+
124
+ def build_capture_or_credit_request(money, identification, options)
125
+ result = new_transaction
126
+
127
+ add_amount(result, money, options)
128
+ add_invoice(result, options)
129
+ add_reference(result, identification)
130
+
131
+ result
132
+ end
133
+
134
+ def build_token_request(credit_card, options)
135
+ result = new_transaction
136
+
137
+ returning result do |r|
138
+ add_credit_card(r, credit_card)
139
+ add_amount(r, 100, options) #need to make an auth request for $1
140
+ add_token_request(r, options)
141
+ end
142
+ end
143
+
144
+ def add_credentials(xml)
145
+ xml.add_element("PostUsername").text = @options[:login]
146
+ xml.add_element("PostPassword").text = @options[:password]
147
+ end
148
+
149
+ def add_reference(xml, identification)
150
+ xml.add_element("DpsTxnRef").text = identification
151
+ end
152
+
153
+ def add_credit_card(xml, credit_card)
154
+ xml.add_element("CardHolderName").text = credit_card.name
155
+ xml.add_element("CardNumber").text = credit_card.number
156
+ xml.add_element("DateExpiry").text = format_date(credit_card.month, credit_card.year)
157
+
158
+ if credit_card.verification_value?
159
+ xml.add_element("Cvc2").text = credit_card.verification_value
160
+ end
161
+
162
+ if requires_start_date_or_issue_number?(credit_card)
163
+ xml.add_element("DateStart").text = format_date(credit_card.start_month, credit_card.start_year) unless credit_card.start_month.blank? || credit_card.start_year.blank?
164
+ xml.add_element("IssueNumber").text = credit_card.issue_number unless credit_card.issue_number.blank?
165
+ end
166
+ end
167
+
168
+ def add_billing_token(xml, token)
169
+ xml.add_element("DpsBillingId").text = token
170
+ end
171
+
172
+ def add_token_request(xml, options)
173
+ xml.add_element("BillingId").text = options[:billing_id] if options[:billing_id]
174
+ xml.add_element("EnableAddBillCard").text = 1
175
+ end
176
+
177
+ def add_amount(xml, money, options)
178
+ xml.add_element("Amount").text = amount(money)
179
+ xml.add_element("InputCurrency").text = options[:currency] || currency(money)
180
+ end
181
+
182
+ def add_transaction_type(xml, action)
183
+ xml.add_element("TxnType").text = TRANSACTIONS[action]
184
+ end
185
+
186
+ def add_invoice(xml, options)
187
+ xml.add_element("TxnId").text = options[:order_id] unless options[:order_id].blank?
188
+ xml.add_element("MerchantReference").text = options[:description] unless options[:description].blank?
189
+ end
190
+
191
+ def add_address_verification_data(xml, options)
192
+ address = options[:billing_address] || options[:address]
193
+ return if address.nil?
194
+
195
+ xml.add_element("EnableAvsData").text = 0
196
+ xml.add_element("AvsAction").text = 0
197
+
198
+ xml.add_element("AvsStreetAddress").text = address[:address1]
199
+ xml.add_element("AvsPostCode").text = address[:zip]
200
+ end
201
+
202
+ def new_transaction
203
+ REXML::Document.new.add_element("Txn")
204
+ end
205
+
206
+ # Take in the request and post it to DPS
207
+ def commit(action, request)
208
+ add_credentials(request)
209
+ add_transaction_type(request, action)
210
+
211
+ # Next, post it to the server
212
+ response = ssl_post(PAYMENT_URL, request.to_s, POST_HEADERS)
213
+
214
+ # Parse the XML response
215
+ @response = parse_response(response)
216
+
217
+ success = @response[:success] == APPROVED
218
+ test = @response[:test_mode] == '1'
219
+
220
+ # Return a response
221
+ PaymentExpressResponse.new(success, @response[:response_text], @response,
222
+ :test => test,
223
+ :authorization => @response[:dps_txn_ref]
224
+ )
225
+ end
226
+
227
+ # Response XML documentation: http://www.paymentexpress.com/technical_resources/ecommerce_nonhosted/pxpost.html#XMLTxnOutput
228
+ def parse_response(xml_string)
229
+ response = {}
230
+
231
+ xml = REXML::Document.new(xml_string)
232
+
233
+ # Gather all root elements such as HelpText
234
+ xml.elements.each('Txn/*') do |element|
235
+ response[element.name.underscore.to_sym] = element.text unless element.name == 'Transaction'
236
+ end
237
+
238
+ # Gather all transaction elements and prefix with "account_"
239
+ # So we could access the MerchantResponseText by going
240
+ # response[account_merchant_response_text]
241
+ xml.elements.each('Txn/Transaction/*') do |element|
242
+ response[element.name.underscore.to_sym] = element.text
243
+ end
244
+
245
+ response
246
+ end
247
+
248
+ def format_date(month, year)
249
+ "#{format(month, :two_digits)}#{format(year, :two_digits)}"
250
+ end
251
+ end
252
+
253
+ class PaymentExpressResponse < Response
254
+ # add a method to response so we can easily get the token
255
+ # for Validate transactions
256
+ def token
257
+ @params["billing_id"] || @params["dps_billing_id"]
258
+ end
259
+ end
260
+ end
261
+ end