start_activemerchant 1.50.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 (218) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG +1769 -0
  3. data/CONTRIBUTORS +540 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +226 -0
  6. data/lib/active_merchant.rb +67 -0
  7. data/lib/active_merchant/billing.rb +15 -0
  8. data/lib/active_merchant/billing/apple_pay_payment_token.rb +22 -0
  9. data/lib/active_merchant/billing/avs_result.rb +98 -0
  10. data/lib/active_merchant/billing/base.rb +72 -0
  11. data/lib/active_merchant/billing/check.rb +76 -0
  12. data/lib/active_merchant/billing/compatibility.rb +120 -0
  13. data/lib/active_merchant/billing/credit_card.rb +404 -0
  14. data/lib/active_merchant/billing/credit_card_formatting.rb +24 -0
  15. data/lib/active_merchant/billing/credit_card_methods.rb +195 -0
  16. data/lib/active_merchant/billing/cvv_result.rb +38 -0
  17. data/lib/active_merchant/billing/gateway.rb +291 -0
  18. data/lib/active_merchant/billing/gateways.rb +14 -0
  19. data/lib/active_merchant/billing/gateways/allied_wallet.rb +203 -0
  20. data/lib/active_merchant/billing/gateways/app55.rb +176 -0
  21. data/lib/active_merchant/billing/gateways/authorize_net.rb +510 -0
  22. data/lib/active_merchant/billing/gateways/authorize_net_arb.rb +417 -0
  23. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +976 -0
  24. data/lib/active_merchant/billing/gateways/axcessms.rb +181 -0
  25. data/lib/active_merchant/billing/gateways/balanced.rb +256 -0
  26. data/lib/active_merchant/billing/gateways/bank_frick.rb +225 -0
  27. data/lib/active_merchant/billing/gateways/banwire.rb +105 -0
  28. data/lib/active_merchant/billing/gateways/barclays_epdq.rb +314 -0
  29. data/lib/active_merchant/billing/gateways/barclays_epdq_extra_plus.rb +15 -0
  30. data/lib/active_merchant/billing/gateways/be2bill.rb +131 -0
  31. data/lib/active_merchant/billing/gateways/beanstream.rb +192 -0
  32. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +389 -0
  33. data/lib/active_merchant/billing/gateways/beanstream_interac.rb +58 -0
  34. data/lib/active_merchant/billing/gateways/blue_pay.rb +506 -0
  35. data/lib/active_merchant/billing/gateways/bogus.rb +140 -0
  36. data/lib/active_merchant/billing/gateways/borgun.rb +211 -0
  37. data/lib/active_merchant/billing/gateways/bpoint.rb +277 -0
  38. data/lib/active_merchant/billing/gateways/braintree.rb +19 -0
  39. data/lib/active_merchant/billing/gateways/braintree/braintree_common.rb +9 -0
  40. data/lib/active_merchant/billing/gateways/braintree_blue.rb +574 -0
  41. data/lib/active_merchant/billing/gateways/braintree_orange.rb +20 -0
  42. data/lib/active_merchant/billing/gateways/bridge_pay.rb +189 -0
  43. data/lib/active_merchant/billing/gateways/card_save.rb +23 -0
  44. data/lib/active_merchant/billing/gateways/card_stream.rb +238 -0
  45. data/lib/active_merchant/billing/gateways/cashnet.rb +202 -0
  46. data/lib/active_merchant/billing/gateways/cc5.rb +201 -0
  47. data/lib/active_merchant/billing/gateways/cecabank.rb +229 -0
  48. data/lib/active_merchant/billing/gateways/cenpos.rb +262 -0
  49. data/lib/active_merchant/billing/gateways/certo_direct.rb +278 -0
  50. data/lib/active_merchant/billing/gateways/checkout.rb +216 -0
  51. data/lib/active_merchant/billing/gateways/checkout_v2.rb +200 -0
  52. data/lib/active_merchant/billing/gateways/commercegate.rb +143 -0
  53. data/lib/active_merchant/billing/gateways/conekta.rb +210 -0
  54. data/lib/active_merchant/billing/gateways/cyber_source.rb +720 -0
  55. data/lib/active_merchant/billing/gateways/data_cash.rb +600 -0
  56. data/lib/active_merchant/billing/gateways/dibs.rb +206 -0
  57. data/lib/active_merchant/billing/gateways/efsnet.rb +219 -0
  58. data/lib/active_merchant/billing/gateways/elavon.rb +348 -0
  59. data/lib/active_merchant/billing/gateways/epay.rb +274 -0
  60. data/lib/active_merchant/billing/gateways/evo_ca.rb +308 -0
  61. data/lib/active_merchant/billing/gateways/eway.rb +214 -0
  62. data/lib/active_merchant/billing/gateways/eway_managed.rb +291 -0
  63. data/lib/active_merchant/billing/gateways/eway_rapid.rb +522 -0
  64. data/lib/active_merchant/billing/gateways/exact.rb +227 -0
  65. data/lib/active_merchant/billing/gateways/ezic.rb +206 -0
  66. data/lib/active_merchant/billing/gateways/fat_zebra.rb +213 -0
  67. data/lib/active_merchant/billing/gateways/federated_canada.rb +160 -0
  68. data/lib/active_merchant/billing/gateways/finansbank.rb +23 -0
  69. data/lib/active_merchant/billing/gateways/first_giving.rb +143 -0
  70. data/lib/active_merchant/billing/gateways/first_pay.rb +160 -0
  71. data/lib/active_merchant/billing/gateways/firstdata_e4.rb +413 -0
  72. data/lib/active_merchant/billing/gateways/flo2cash.rb +215 -0
  73. data/lib/active_merchant/billing/gateways/flo2cash_simple.rb +20 -0
  74. data/lib/active_merchant/billing/gateways/garanti.rb +261 -0
  75. data/lib/active_merchant/billing/gateways/global_transport.rb +179 -0
  76. data/lib/active_merchant/billing/gateways/hdfc.rb +207 -0
  77. data/lib/active_merchant/billing/gateways/hps.rb +287 -0
  78. data/lib/active_merchant/billing/gateways/iats_payments.rb +277 -0
  79. data/lib/active_merchant/billing/gateways/ideal/ideal_base.rb +246 -0
  80. data/lib/active_merchant/billing/gateways/ideal/ideal_rabobank.pem +13 -0
  81. data/lib/active_merchant/billing/gateways/ideal/ideal_response.rb +29 -0
  82. data/lib/active_merchant/billing/gateways/ideal_rabobank.rb +66 -0
  83. data/lib/active_merchant/billing/gateways/inspire.rb +219 -0
  84. data/lib/active_merchant/billing/gateways/instapay.rb +163 -0
  85. data/lib/active_merchant/billing/gateways/ipp.rb +175 -0
  86. data/lib/active_merchant/billing/gateways/iridium.rb +457 -0
  87. data/lib/active_merchant/billing/gateways/itransact.rb +448 -0
  88. data/lib/active_merchant/billing/gateways/jetpay.rb +275 -0
  89. data/lib/active_merchant/billing/gateways/linkpoint.rb +438 -0
  90. data/lib/active_merchant/billing/gateways/litle.rb +345 -0
  91. data/lib/active_merchant/billing/gateways/maxipago.rb +197 -0
  92. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +170 -0
  93. data/lib/active_merchant/billing/gateways/merchant_one.rb +114 -0
  94. data/lib/active_merchant/billing/gateways/merchant_partners.rb +245 -0
  95. data/lib/active_merchant/billing/gateways/merchant_ware.rb +319 -0
  96. data/lib/active_merchant/billing/gateways/merchant_ware_version_four.rb +268 -0
  97. data/lib/active_merchant/billing/gateways/merchant_warrior.rb +195 -0
  98. data/lib/active_merchant/billing/gateways/mercury.rb +326 -0
  99. data/lib/active_merchant/billing/gateways/metrics_global.rb +303 -0
  100. data/lib/active_merchant/billing/gateways/migs.rb +280 -0
  101. data/lib/active_merchant/billing/gateways/migs/migs_codes.rb +100 -0
  102. data/lib/active_merchant/billing/gateways/modern_payments.rb +37 -0
  103. data/lib/active_merchant/billing/gateways/modern_payments_cim.rb +219 -0
  104. data/lib/active_merchant/billing/gateways/monei.rb +307 -0
  105. data/lib/active_merchant/billing/gateways/moneris.rb +309 -0
  106. data/lib/active_merchant/billing/gateways/moneris_us.rb +298 -0
  107. data/lib/active_merchant/billing/gateways/money_movers.rb +152 -0
  108. data/lib/active_merchant/billing/gateways/nab_transact.rb +290 -0
  109. data/lib/active_merchant/billing/gateways/net_registry.rb +198 -0
  110. data/lib/active_merchant/billing/gateways/netaxept.rb +181 -0
  111. data/lib/active_merchant/billing/gateways/netbilling.rb +224 -0
  112. data/lib/active_merchant/billing/gateways/netpay.rb +223 -0
  113. data/lib/active_merchant/billing/gateways/network_merchants.rb +242 -0
  114. data/lib/active_merchant/billing/gateways/nmi.rb +256 -0
  115. data/lib/active_merchant/billing/gateways/ogone.rb +435 -0
  116. data/lib/active_merchant/billing/gateways/omise.rb +319 -0
  117. data/lib/active_merchant/billing/gateways/openpay.rb +194 -0
  118. data/lib/active_merchant/billing/gateways/optimal_payment.rb +314 -0
  119. data/lib/active_merchant/billing/gateways/orbital.rb +834 -0
  120. data/lib/active_merchant/billing/gateways/orbital/orbital_soft_descriptors.rb +47 -0
  121. data/lib/active_merchant/billing/gateways/pac_net_raven.rb +207 -0
  122. data/lib/active_merchant/billing/gateways/pago_facil.rb +122 -0
  123. data/lib/active_merchant/billing/gateways/pay_conex.rb +246 -0
  124. data/lib/active_merchant/billing/gateways/pay_gate_xml.rb +277 -0
  125. data/lib/active_merchant/billing/gateways/pay_hub.rb +213 -0
  126. data/lib/active_merchant/billing/gateways/pay_junction.rb +390 -0
  127. data/lib/active_merchant/billing/gateways/pay_secure.rb +112 -0
  128. data/lib/active_merchant/billing/gateways/paybox_direct.rb +188 -0
  129. data/lib/active_merchant/billing/gateways/payex.rb +412 -0
  130. data/lib/active_merchant/billing/gateways/payflow.rb +308 -0
  131. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +220 -0
  132. data/lib/active_merchant/billing/gateways/payflow/payflow_express_response.rb +39 -0
  133. data/lib/active_merchant/billing/gateways/payflow/payflow_response.rb +13 -0
  134. data/lib/active_merchant/billing/gateways/payflow_express.rb +224 -0
  135. data/lib/active_merchant/billing/gateways/payflow_express_uk.rb +15 -0
  136. data/lib/active_merchant/billing/gateways/payflow_uk.rb +21 -0
  137. data/lib/active_merchant/billing/gateways/payment_express.rb +353 -0
  138. data/lib/active_merchant/billing/gateways/paymill.rb +282 -0
  139. data/lib/active_merchant/billing/gateways/paypal.rb +129 -0
  140. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +679 -0
  141. data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +65 -0
  142. data/lib/active_merchant/billing/gateways/paypal/paypal_recurring_api.rb +262 -0
  143. data/lib/active_merchant/billing/gateways/paypal_ca.rb +13 -0
  144. data/lib/active_merchant/billing/gateways/paypal_digital_goods.rb +44 -0
  145. data/lib/active_merchant/billing/gateways/paypal_express.rb +264 -0
  146. data/lib/active_merchant/billing/gateways/paypal_express_common.rb +30 -0
  147. data/lib/active_merchant/billing/gateways/payscout.rb +162 -0
  148. data/lib/active_merchant/billing/gateways/paystation.rb +199 -0
  149. data/lib/active_merchant/billing/gateways/payu_in.rb +247 -0
  150. data/lib/active_merchant/billing/gateways/payway.rb +207 -0
  151. data/lib/active_merchant/billing/gateways/pin.rb +207 -0
  152. data/lib/active_merchant/billing/gateways/plugnpay.rb +283 -0
  153. data/lib/active_merchant/billing/gateways/psigate.rb +216 -0
  154. data/lib/active_merchant/billing/gateways/psl_card.rb +303 -0
  155. data/lib/active_merchant/billing/gateways/qbms.rb +292 -0
  156. data/lib/active_merchant/billing/gateways/quantum.rb +276 -0
  157. data/lib/active_merchant/billing/gateways/quickbooks.rb +280 -0
  158. data/lib/active_merchant/billing/gateways/quickpay.rb +26 -0
  159. data/lib/active_merchant/billing/gateways/quickpay/quickpay_common.rb +188 -0
  160. data/lib/active_merchant/billing/gateways/quickpay/quickpay_v10.rb +240 -0
  161. data/lib/active_merchant/billing/gateways/quickpay/quickpay_v4to7.rb +227 -0
  162. data/lib/active_merchant/billing/gateways/qvalent.rb +179 -0
  163. data/lib/active_merchant/billing/gateways/realex.rb +298 -0
  164. data/lib/active_merchant/billing/gateways/redsys.rb +406 -0
  165. data/lib/active_merchant/billing/gateways/s5.rb +226 -0
  166. data/lib/active_merchant/billing/gateways/sage.rb +173 -0
  167. data/lib/active_merchant/billing/gateways/sage/sage_bankcard.rb +89 -0
  168. data/lib/active_merchant/billing/gateways/sage/sage_core.rb +115 -0
  169. data/lib/active_merchant/billing/gateways/sage/sage_vault.rb +149 -0
  170. data/lib/active_merchant/billing/gateways/sage/sage_virtual_check.rb +97 -0
  171. data/lib/active_merchant/billing/gateways/sage_pay.rb +399 -0
  172. data/lib/active_merchant/billing/gateways/sallie_mae.rb +143 -0
  173. data/lib/active_merchant/billing/gateways/secure_net.rb +263 -0
  174. data/lib/active_merchant/billing/gateways/secure_pay.rb +201 -0
  175. data/lib/active_merchant/billing/gateways/secure_pay_au.rb +281 -0
  176. data/lib/active_merchant/billing/gateways/secure_pay_tech.rb +105 -0
  177. data/lib/active_merchant/billing/gateways/skip_jack.rb +451 -0
  178. data/lib/active_merchant/billing/gateways/smart_ps.rb +283 -0
  179. data/lib/active_merchant/billing/gateways/so_easy_pay.rb +194 -0
  180. data/lib/active_merchant/billing/gateways/spreedly_core.rb +247 -0
  181. data/lib/active_merchant/billing/gateways/stripe.rb +489 -0
  182. data/lib/active_merchant/billing/gateways/swipe_checkout.rb +157 -0
  183. data/lib/active_merchant/billing/gateways/tns.rb +227 -0
  184. data/lib/active_merchant/billing/gateways/trans_first.rb +126 -0
  185. data/lib/active_merchant/billing/gateways/transax.rb +23 -0
  186. data/lib/active_merchant/billing/gateways/transnational.rb +10 -0
  187. data/lib/active_merchant/billing/gateways/trust_commerce.rb +416 -0
  188. data/lib/active_merchant/billing/gateways/usa_epay.rb +25 -0
  189. data/lib/active_merchant/billing/gateways/usa_epay_advanced.rb +1516 -0
  190. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +259 -0
  191. data/lib/active_merchant/billing/gateways/vanco.rb +280 -0
  192. data/lib/active_merchant/billing/gateways/verifi.rb +225 -0
  193. data/lib/active_merchant/billing/gateways/viaklix.rb +183 -0
  194. data/lib/active_merchant/billing/gateways/webpay.rb +97 -0
  195. data/lib/active_merchant/billing/gateways/wepay.rb +205 -0
  196. data/lib/active_merchant/billing/gateways/wirecard.rb +420 -0
  197. data/lib/active_merchant/billing/gateways/worldpay.rb +331 -0
  198. data/lib/active_merchant/billing/gateways/worldpay_online_payments.rb +204 -0
  199. data/lib/active_merchant/billing/gateways/worldpay_us.rb +181 -0
  200. data/lib/active_merchant/billing/model.rb +30 -0
  201. data/lib/active_merchant/billing/network_tokenization_credit_card.rb +24 -0
  202. data/lib/active_merchant/billing/payment_token.rb +21 -0
  203. data/lib/active_merchant/billing/rails.rb +3 -0
  204. data/lib/active_merchant/billing/response.rb +92 -0
  205. data/lib/active_merchant/connection.rb +172 -0
  206. data/lib/active_merchant/country.rb +332 -0
  207. data/lib/active_merchant/empty.rb +20 -0
  208. data/lib/active_merchant/errors.rb +35 -0
  209. data/lib/active_merchant/network_connection_retries.rb +79 -0
  210. data/lib/active_merchant/post_data.rb +24 -0
  211. data/lib/active_merchant/posts_data.rb +84 -0
  212. data/lib/active_merchant/version.rb +3 -0
  213. data/lib/activemerchant.rb +1 -0
  214. data/lib/certs/cacert.pem +3866 -0
  215. data/lib/support/gateway_support.rb +71 -0
  216. data/lib/support/outbound_hosts.rb +28 -0
  217. data/lib/support/ssl_verify.rb +93 -0
  218. metadata +387 -0
@@ -0,0 +1,331 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class WorldpayGateway < Gateway
4
+ self.test_url = 'https://secure-test.worldpay.com/jsp/merchant/xml/paymentService.jsp'
5
+ self.live_url = 'https://secure.worldpay.com/jsp/merchant/xml/paymentService.jsp'
6
+
7
+ self.default_currency = 'GBP'
8
+ self.money_format = :cents
9
+ self.supported_countries = %w(HK GB AU AD BE CH CY CZ DE DK ES FI FR GI GR HU IE IL IT LI LU MC MT NL NO NZ PL PT SE SG SI SM TR UM VA)
10
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :maestro, :laser, :switch]
11
+ self.homepage_url = 'http://www.worldpay.com/'
12
+ self.display_name = 'Worldpay Global'
13
+
14
+ CARD_CODES = {
15
+ 'visa' => 'VISA-SSL',
16
+ 'master' => 'ECMC-SSL',
17
+ 'discover' => 'DISCOVER-SSL',
18
+ 'american_express' => 'AMEX-SSL',
19
+ 'jcb' => 'JCB-SSL',
20
+ 'maestro' => 'MAESTRO-SSL',
21
+ 'laser' => 'LASER-SSL',
22
+ 'diners_club' => 'DINERS-SSL',
23
+ 'switch' => 'MAESTRO-SSL'
24
+ }
25
+
26
+ def initialize(options = {})
27
+ requires!(options, :login, :password)
28
+ super
29
+ end
30
+
31
+ def purchase(money, payment_method, options = {})
32
+ MultiResponse.run do |r|
33
+ r.process{authorize(money, payment_method, options)}
34
+ r.process{capture(money, r.authorization, options.merge(:authorization_validated => true))}
35
+ end
36
+ end
37
+
38
+ def authorize(money, payment_method, options = {})
39
+ requires!(options, :order_id)
40
+ authorize_request(money, payment_method, options)
41
+ end
42
+
43
+ def capture(money, authorization, options = {})
44
+ MultiResponse.run do |r|
45
+ r.process{inquire_request(authorization, options, "AUTHORISED")} unless options[:authorization_validated]
46
+ if r.params
47
+ authorization_currency = r.params['amount_currency_code']
48
+ options = options.merge(:currency => authorization_currency) if authorization_currency.present?
49
+ end
50
+ r.process{capture_request(money, authorization, options)}
51
+ end
52
+ end
53
+
54
+ def void(authorization, options = {})
55
+ MultiResponse.run do |r|
56
+ r.process{inquire_request(authorization, options, "AUTHORISED")}
57
+ r.process{cancel_request(authorization, options)}
58
+ end
59
+ end
60
+
61
+ def refund(money, authorization, options = {})
62
+ MultiResponse.run do |r|
63
+ r.process{inquire_request(authorization, options, "CAPTURED", "SETTLED", "SETTLED_BY_MERCHANT")}
64
+ r.process{refund_request(money, authorization, options)}
65
+ end
66
+ end
67
+
68
+ private
69
+
70
+ def authorize_request(money, payment_method, options)
71
+ commit('authorize', build_authorization_request(money, payment_method, options), "AUTHORISED")
72
+ end
73
+
74
+ def capture_request(money, authorization, options)
75
+ commit('capture', build_capture_request(money, authorization, options), :ok)
76
+ end
77
+
78
+ def cancel_request(authorization, options)
79
+ commit('cancel', build_void_request(authorization, options), :ok)
80
+ end
81
+
82
+ def inquire_request(authorization, options, *success_criteria)
83
+ commit('inquiry', build_order_inquiry_request(authorization, options), *success_criteria)
84
+ end
85
+
86
+ def refund_request(money, authorization, options)
87
+ commit('refund', build_refund_request(money, authorization, options), :ok)
88
+ end
89
+
90
+ def build_request
91
+ xml = Builder::XmlMarkup.new :indent => 2
92
+ xml.instruct! :xml, :encoding => 'UTF-8'
93
+ xml.declare! :DOCTYPE, :paymentService, :PUBLIC, "-//WorldPay//DTD WorldPay PaymentService v1//EN", "http://dtd.worldpay.com/paymentService_v1.dtd"
94
+ xml.tag! 'paymentService', 'version' => "1.4", 'merchantCode' => @options[:login] do
95
+ yield xml
96
+ end
97
+ xml.target!
98
+ end
99
+
100
+ def build_order_modify_request(authorization)
101
+ build_request do |xml|
102
+ xml.tag! 'modify' do
103
+ xml.tag! 'orderModification', 'orderCode' => authorization do
104
+ yield xml
105
+ end
106
+ end
107
+ end
108
+ end
109
+
110
+ def build_order_inquiry_request(authorization, options)
111
+ build_request do |xml|
112
+ xml.tag! 'inquiry' do
113
+ xml.tag! 'orderInquiry', 'orderCode' => authorization
114
+ end
115
+ end
116
+ end
117
+
118
+ def build_authorization_request(money, payment_method, options)
119
+ build_request do |xml|
120
+ xml.tag! 'submit' do
121
+ xml.tag! 'order', {'orderCode' => options[:order_id], 'installationId' => @options[:inst_id]}.reject{|_,v| !v} do
122
+ xml.description(options[:description].blank? ? "Purchase" : options[:description])
123
+ add_amount(xml, money, options)
124
+ if options[:order_content]
125
+ xml.tag! 'orderContent' do
126
+ xml.cdata! options[:order_content]
127
+ end
128
+ end
129
+ add_payment_method(xml, money, payment_method, options)
130
+ add_email(xml, options)
131
+ end
132
+ end
133
+ end
134
+ end
135
+
136
+ def build_capture_request(money, authorization, options)
137
+ build_order_modify_request(authorization) do |xml|
138
+ xml.tag! 'capture' do
139
+ time = Time.now
140
+ xml.tag! 'date', 'dayOfMonth' => time.day, 'month' => time.month, 'year'=> time.year
141
+ add_amount(xml, money, options)
142
+ end
143
+ end
144
+ end
145
+
146
+ def build_void_request(authorization, options)
147
+ build_order_modify_request(authorization) do |xml|
148
+ xml.tag! 'cancel'
149
+ end
150
+ end
151
+
152
+ def build_refund_request(money, authorization, options)
153
+ build_order_modify_request(authorization) do |xml|
154
+ xml.tag! 'refund' do
155
+ add_amount(xml, money, options.merge(:debit_credit_indicator => "credit"))
156
+ end
157
+ end
158
+ end
159
+
160
+ def add_amount(xml, money, options)
161
+ currency = options[:currency] || currency(money)
162
+ amount = localized_amount(money, currency)
163
+
164
+ amount_hash = {
165
+ :value => amount,
166
+ 'currencyCode' => currency,
167
+ 'exponent' => 2
168
+ }
169
+
170
+ if options[:debit_credit_indicator]
171
+ amount_hash.merge!('debitCreditIndicator' => options[:debit_credit_indicator])
172
+ end
173
+
174
+ xml.tag! 'amount', amount_hash
175
+ end
176
+
177
+ def add_payment_method(xml, amount, payment_method, options)
178
+ if payment_method.is_a?(String)
179
+ if options[:merchant_code]
180
+ xml.tag! 'payAsOrder', 'orderCode' => payment_method, 'merchantCode' => options[:merchant_code] do
181
+ add_amount(xml, amount, options)
182
+ end
183
+ else
184
+ xml.tag! 'payAsOrder', 'orderCode' => payment_method do
185
+ add_amount(xml, amount, options)
186
+ end
187
+ end
188
+ else
189
+ xml.tag! 'paymentDetails' do
190
+ xml.tag! CARD_CODES[card_brand(payment_method)] do
191
+ xml.tag! 'cardNumber', payment_method.number
192
+ xml.tag! 'expiryDate' do
193
+ xml.tag! 'date', 'month' => format(payment_method.month, :two_digits), 'year' => format(payment_method.year, :four_digits)
194
+ end
195
+
196
+ xml.tag! 'cardHolderName', payment_method.name
197
+ xml.tag! 'cvc', payment_method.verification_value
198
+
199
+ add_address(xml, (options[:billing_address] || options[:address]))
200
+ end
201
+ if options[:ip]
202
+ xml.tag! 'session', 'shopperIPAddress' => options[:ip]
203
+ end
204
+ end
205
+ end
206
+ end
207
+
208
+ def add_email(xml, options)
209
+ return unless options[:email]
210
+ xml.tag! 'shopper' do
211
+ xml.tag! 'shopperEmailAddress', options[:email]
212
+ end
213
+ end
214
+
215
+ def add_address(xml, address)
216
+ address = address_with_defaults(address)
217
+
218
+ xml.tag! 'cardAddress' do
219
+ xml.tag! 'address' do
220
+ if m = /^\s*([^\s]+)\s+(.+)$/.match(address[:name])
221
+ xml.tag! 'firstName', m[1]
222
+ xml.tag! 'lastName', m[2]
223
+ end
224
+ xml.tag! 'address1', address[:address1]
225
+ xml.tag! 'address2', address[:address2] if address[:address2]
226
+ xml.tag! 'postalCode', address[:zip]
227
+ xml.tag! 'city', address[:city]
228
+ xml.tag! 'state', address[:state]
229
+ xml.tag! 'countryCode', address[:country]
230
+ xml.tag! 'telephoneNumber', address[:phone] if address[:phone]
231
+ end
232
+ end
233
+ end
234
+
235
+ def address_with_defaults(address)
236
+ address ||= {}
237
+ address.delete_if { |_, v| v.blank? }
238
+ address.reverse_merge!(default_address)
239
+ end
240
+
241
+ def default_address
242
+ {
243
+ address1: 'N/A',
244
+ zip: '0000',
245
+ city: 'N/A',
246
+ state: 'N/A',
247
+ country: 'US'
248
+ }
249
+ end
250
+
251
+ def parse(action, xml)
252
+ parse_element({:action => action}, REXML::Document.new(xml))
253
+ end
254
+
255
+ def parse_element(raw, node)
256
+ node.attributes.each do |k, v|
257
+ raw["#{node.name.underscore}_#{k.underscore}".to_sym] = v
258
+ end
259
+ if node.has_elements?
260
+ raw[node.name.underscore.to_sym] = true unless node.name.blank?
261
+ node.elements.each{|e| parse_element(raw, e) }
262
+ else
263
+ raw[node.name.underscore.to_sym] = node.text unless node.text.nil?
264
+ end
265
+ raw
266
+ end
267
+
268
+ def commit(action, request, *success_criteria)
269
+ xmr = ssl_post(url, request, 'Content-Type' => 'text/xml', 'Authorization' => encoded_credentials)
270
+ raw = parse(action, xmr)
271
+ success, message = success_and_message_from(raw, success_criteria)
272
+
273
+ Response.new(
274
+ success,
275
+ message,
276
+ raw,
277
+ :authorization => authorization_from(raw),
278
+ :test => test?)
279
+
280
+ rescue ActiveMerchant::ResponseError => e
281
+ if e.response.code.to_s == "401"
282
+ return Response.new(false, "Invalid credentials", {}, :test => test?)
283
+ else
284
+ raise e
285
+ end
286
+ end
287
+
288
+ def url
289
+ test? ? self.test_url : self.live_url
290
+ end
291
+
292
+ # success_criteria can be:
293
+ # - a string or an array of strings (if one of many responses)
294
+ # - An array of strings if one of many responses could be considered a
295
+ # success.
296
+ def success_and_message_from(raw, success_criteria)
297
+ success = (success_criteria.include?(raw[:last_event]) || raw[:ok].present?)
298
+ if success
299
+ message = "SUCCESS"
300
+ else
301
+ message = (raw[:iso8583_return_code_description] || raw[:error] || required_status_message(raw, success_criteria))
302
+ end
303
+
304
+ [ success, message ]
305
+ end
306
+
307
+ def required_status_message(raw, success_criteria)
308
+ if(!success_criteria.include?(raw[:last_event]))
309
+ "A transaction status of #{success_criteria.collect{|c| "'#{c}'"}.join(" or ")} is required."
310
+ end
311
+ end
312
+
313
+ def authorization_from(raw)
314
+ pair = raw.detect{|k,v| k.to_s =~ /_order_code$/}
315
+ (pair ? pair.last : nil)
316
+ end
317
+
318
+ def encoded_credentials
319
+ credentials = "#{@options[:login]}:#{@options[:password]}"
320
+ "Basic #{[credentials].pack('m').strip}"
321
+ end
322
+
323
+ def localized_amount(money, currency)
324
+ amount = amount(money)
325
+ return amount unless CURRENCIES_WITHOUT_FRACTIONS.include?(currency.to_s)
326
+
327
+ amount.to_i / 100 * 100
328
+ end
329
+ end
330
+ end
331
+ end
@@ -0,0 +1,204 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class WorldpayOnlinePaymentsGateway < Gateway
4
+ self.live_url = 'https://api.worldpay.com/v1/'
5
+
6
+ self.default_currency = 'GBP'
7
+
8
+ self.money_format = :cents
9
+
10
+ self.supported_countries = %w(HK US GB AU AD BE CH CY CZ DE DK ES FI FR GI GR HU IE IL IT LI LU MC MT NL NO NZ PL PT SE SG SI SM TR UM VA)
11
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :maestro, :laser, :switch]
12
+
13
+ self.homepage_url = 'http://online.worldpay.com'
14
+ self.display_name = 'Worldpay Online Payments'
15
+
16
+ def initialize(options={})
17
+ requires!(options, :client_key, :service_key)
18
+ @client_key = options[:client_key]
19
+ @service_key = options[:service_key]
20
+ super
21
+ end
22
+
23
+ def authorize(money, credit_card, options={})
24
+ response = create_token(true, credit_card.first_name+' '+credit_card.last_name, credit_card.month, credit_card.year, credit_card.number, credit_card.verification_value)
25
+ if response.success?
26
+ options[:authorizeOnly] = true
27
+ post = create_post_for_auth_or_purchase(response.authorization, money, options)
28
+ response = commit(:post, 'orders', post)
29
+ end
30
+ response
31
+ end
32
+
33
+ def capture(money, authorization, options={})
34
+ if authorization
35
+ commit(:post, "orders/#{CGI.escape(authorization)}/capture", {"captureAmount"=>money}, options)
36
+ else
37
+ Response.new(false,
38
+ 'FAILED',
39
+ 'FAILED',
40
+ :test => test?,
41
+ :authorization => false,
42
+ :avs_result => {},
43
+ :cvv_result => {},
44
+ :error_code => false
45
+ )
46
+ end
47
+ end
48
+
49
+ def purchase(money, credit_card, options={})
50
+ response = create_token(true, credit_card.first_name+' '+credit_card.last_name, credit_card.month, credit_card.year, credit_card.number, credit_card.verification_value)
51
+ if response.success?
52
+ post = create_post_for_auth_or_purchase(response.authorization, money, options)
53
+ response = commit(:post, 'orders', post, options)
54
+ end
55
+ response
56
+ end
57
+
58
+ def refund(money, orderCode, options={})
59
+ obj = money ? {"refundAmount" => money} : {}
60
+ commit(:post, "orders/#{CGI.escape(orderCode)}/refund", obj, options)
61
+ end
62
+
63
+ def void(orderCode, options={})
64
+ response = commit(:delete, "orders/#{CGI.escape(orderCode)}", nil, options)
65
+ if !response.success? && (response.params && response.params['customCode'] != 'ORDER_NOT_FOUND')
66
+ response = refund(nil, orderCode)
67
+ end
68
+ response
69
+ end
70
+
71
+ def verify(credit_card, options={})
72
+ authorize(0, credit_card, options)
73
+ end
74
+
75
+ private
76
+
77
+ def create_token(reusable, name, exp_month, exp_year, number, cvc)
78
+ obj = {
79
+ "reusable"=> reusable,
80
+ "paymentMethod"=> {
81
+ "type"=> "Card",
82
+ "name"=> name,
83
+ "expiryMonth"=> exp_month,
84
+ "expiryYear"=> exp_year,
85
+ "cardNumber"=> number,
86
+ "cvc"=> cvc
87
+ },
88
+ "clientKey"=> @client_key
89
+ }
90
+ token_response = commit(:post, 'tokens', obj, {'Authorization' => @service_key})
91
+ token_response
92
+ end
93
+
94
+ def create_post_for_auth_or_purchase(token, money, options)
95
+ {
96
+ "token" => token,
97
+ "orderDescription" => options[:description] || 'Worldpay Order',
98
+ "amount" => money,
99
+ "currencyCode" => options[:currency] || default_currency,
100
+ "name" => options[:billing_address]&&options[:billing_address][:name] ? options[:billing_address][:name] : '',
101
+ "billingAddress" => {
102
+ "address1"=>options[:billing_address]&&options[:billing_address][:address1] ? options[:billing_address][:address1] : '',
103
+ "address2"=>options[:billing_address]&&options[:billing_address][:address2] ? options[:billing_address][:address2] : '',
104
+ "address3"=>"",
105
+ "postalCode"=>options[:billing_address]&&options[:billing_address][:zip] ? options[:billing_address][:zip] : '',
106
+ "city"=>options[:billing_address]&&options[:billing_address][:city] ? options[:billing_address][:city] : '',
107
+ "state"=>options[:billing_address]&&options[:billing_address][:state] ? options[:billing_address][:state] : '',
108
+ "countryCode"=>options[:billing_address]&&options[:billing_address][:country] ? options[:billing_address][:country] : ''
109
+ },
110
+ "customerOrderCode" => options[:order_id],
111
+ "orderType" => "ECOM",
112
+ "authorizeOnly" => options[:authorizeOnly] ? true : false
113
+ }
114
+ end
115
+
116
+ def parse(body)
117
+ body ? JSON.parse(body) : {}
118
+ end
119
+
120
+ def headers(options = {})
121
+ headers = {
122
+ "Authorization" => @service_key,
123
+ "Content-Type" => 'application/json',
124
+ "User-Agent" => "Worldpay/v1 ActiveMerchantBindings/#{ActiveMerchant::VERSION}",
125
+ "X-Worldpay-Client-User-Agent" => user_agent,
126
+ "X-Worldpay-Client-User-Metadata" => {:ip => options[:ip]}.to_json
127
+ }
128
+ if options['Authorization']
129
+ headers['Authorization'] = options['Authorization']
130
+ end
131
+ headers
132
+ end
133
+
134
+ def commit(method, url, parameters=nil, options = {})
135
+ raw_response = response = nil
136
+ success = false
137
+ begin
138
+ json = parameters ? parameters.to_json : nil
139
+
140
+ raw_response = ssl_request(method, self.live_url + url, json, headers(options))
141
+
142
+ if (raw_response != '')
143
+ response = parse(raw_response)
144
+ success = !response.key?("httpStatusCode")
145
+ else
146
+ success = true
147
+ response = {}
148
+ end
149
+
150
+ rescue ResponseError => e
151
+ raw_response = e.response.body
152
+ response = response_error(raw_response)
153
+ rescue JSON::ParserError => e
154
+ response = json_error(raw_response)
155
+ end
156
+
157
+ if response["orderCode"]
158
+ authorization = response["orderCode"]
159
+ elsif response["token"]
160
+ authorization = response["token"]
161
+ else
162
+ authorization = response["message"]
163
+ end
164
+
165
+ Response.new(success,
166
+ success ? "SUCCESS" : response["message"],
167
+ response,
168
+ :test => test?,
169
+ :authorization => authorization,
170
+ :avs_result => {},
171
+ :cvv_result => {},
172
+ :error_code => success ? nil : response["customCode"]
173
+ )
174
+ end
175
+
176
+ def test?
177
+ @service_key[0]=="T" ? true : false
178
+ end
179
+
180
+ def response_error(raw_response)
181
+ begin
182
+ parse(raw_response)
183
+ rescue JSON::ParserError
184
+ json_error(raw_response)
185
+ end
186
+ end
187
+
188
+ def json_error(raw_response)
189
+ msg = 'Invalid response received from the Worldpay Online Payments API. Please contact techsupport.online@worldpay.com if you continue to receive this message.'
190
+ msg += " (The raw response returned by the API was #{raw_response.inspect})"
191
+ {
192
+ "error" => {
193
+ "message" => msg
194
+ }
195
+ }
196
+ end
197
+
198
+ def handle_response(response)
199
+ response.body
200
+ end
201
+
202
+ end
203
+ end
204
+ end