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.
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