aktivemerchant 2.0.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 (193) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG +1596 -0
  3. data/CONTRIBUTORS +511 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +18 -0
  6. data/lib/active_merchant.rb +108 -0
  7. data/lib/active_merchant/billing.rb +13 -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 +352 -0
  14. data/lib/active_merchant/billing/credit_card_formatting.rb +24 -0
  15. data/lib/active_merchant/billing/credit_card_methods.rb +160 -0
  16. data/lib/active_merchant/billing/cvv_result.rb +38 -0
  17. data/lib/active_merchant/billing/gateway.rb +268 -0
  18. data/lib/active_merchant/billing/gateways.rb +17 -0
  19. data/lib/active_merchant/billing/gateways/adyen.rb +209 -0
  20. data/lib/active_merchant/billing/gateways/alfabank.rb +117 -0
  21. data/lib/active_merchant/billing/gateways/app55.rb +176 -0
  22. data/lib/active_merchant/billing/gateways/authorize_net.rb +419 -0
  23. data/lib/active_merchant/billing/gateways/authorize_net_arb.rb +417 -0
  24. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +976 -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 +188 -0
  32. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +393 -0
  33. data/lib/active_merchant/billing/gateways/beanstream_interac.rb +54 -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 +210 -0
  37. data/lib/active_merchant/billing/gateways/braintree.rb +19 -0
  38. data/lib/active_merchant/billing/gateways/braintree/braintree_common.rb +9 -0
  39. data/lib/active_merchant/billing/gateways/braintree_blue.rb +515 -0
  40. data/lib/active_merchant/billing/gateways/braintree_orange.rb +20 -0
  41. data/lib/active_merchant/billing/gateways/bridge_pay.rb +189 -0
  42. data/lib/active_merchant/billing/gateways/card_save.rb +23 -0
  43. data/lib/active_merchant/billing/gateways/card_stream.rb +220 -0
  44. data/lib/active_merchant/billing/gateways/cashnet.rb +191 -0
  45. data/lib/active_merchant/billing/gateways/cc5.rb +201 -0
  46. data/lib/active_merchant/billing/gateways/cecabank.rb +229 -0
  47. data/lib/active_merchant/billing/gateways/certo_direct.rb +278 -0
  48. data/lib/active_merchant/billing/gateways/checkout.rb +213 -0
  49. data/lib/active_merchant/billing/gateways/commercegate.rb +143 -0
  50. data/lib/active_merchant/billing/gateways/conekta.rb +209 -0
  51. data/lib/active_merchant/billing/gateways/cyber_source.rb +709 -0
  52. data/lib/active_merchant/billing/gateways/data_cash.rb +600 -0
  53. data/lib/active_merchant/billing/gateways/efsnet.rb +219 -0
  54. data/lib/active_merchant/billing/gateways/elavon.rb +348 -0
  55. data/lib/active_merchant/billing/gateways/epay.rb +275 -0
  56. data/lib/active_merchant/billing/gateways/evo_ca.rb +308 -0
  57. data/lib/active_merchant/billing/gateways/eway.rb +214 -0
  58. data/lib/active_merchant/billing/gateways/eway_managed.rb +291 -0
  59. data/lib/active_merchant/billing/gateways/eway_rapid.rb +524 -0
  60. data/lib/active_merchant/billing/gateways/exact.rb +218 -0
  61. data/lib/active_merchant/billing/gateways/fat_zebra.rb +173 -0
  62. data/lib/active_merchant/billing/gateways/federated_canada.rb +160 -0
  63. data/lib/active_merchant/billing/gateways/finansbank.rb +23 -0
  64. data/lib/active_merchant/billing/gateways/first_giving.rb +143 -0
  65. data/lib/active_merchant/billing/gateways/first_pay.rb +160 -0
  66. data/lib/active_merchant/billing/gateways/firstdata_e4.rb +355 -0
  67. data/lib/active_merchant/billing/gateways/garanti.rb +257 -0
  68. data/lib/active_merchant/billing/gateways/global_transport.rb +183 -0
  69. data/lib/active_merchant/billing/gateways/hdfc.rb +207 -0
  70. data/lib/active_merchant/billing/gateways/hps.rb +288 -0
  71. data/lib/active_merchant/billing/gateways/iats_payments.rb +251 -0
  72. data/lib/active_merchant/billing/gateways/ideal/ideal_base.rb +246 -0
  73. data/lib/active_merchant/billing/gateways/ideal/ideal_rabobank.pem +13 -0
  74. data/lib/active_merchant/billing/gateways/ideal/ideal_response.rb +29 -0
  75. data/lib/active_merchant/billing/gateways/ideal_rabobank.rb +66 -0
  76. data/lib/active_merchant/billing/gateways/inspire.rb +213 -0
  77. data/lib/active_merchant/billing/gateways/instapay.rb +163 -0
  78. data/lib/active_merchant/billing/gateways/iridium.rb +457 -0
  79. data/lib/active_merchant/billing/gateways/itransact.rb +448 -0
  80. data/lib/active_merchant/billing/gateways/jetpay.rb +275 -0
  81. data/lib/active_merchant/billing/gateways/linkpoint.rb +438 -0
  82. data/lib/active_merchant/billing/gateways/litle.rb +346 -0
  83. data/lib/active_merchant/billing/gateways/maxipago.rb +197 -0
  84. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +170 -0
  85. data/lib/active_merchant/billing/gateways/merchant_one.rb +114 -0
  86. data/lib/active_merchant/billing/gateways/merchant_ware.rb +319 -0
  87. data/lib/active_merchant/billing/gateways/merchant_ware_version_four.rb +268 -0
  88. data/lib/active_merchant/billing/gateways/merchant_warrior.rb +195 -0
  89. data/lib/active_merchant/billing/gateways/mercury.rb +333 -0
  90. data/lib/active_merchant/billing/gateways/metrics_global.rb +303 -0
  91. data/lib/active_merchant/billing/gateways/migs.rb +265 -0
  92. data/lib/active_merchant/billing/gateways/migs/migs_codes.rb +100 -0
  93. data/lib/active_merchant/billing/gateways/modern_payments.rb +37 -0
  94. data/lib/active_merchant/billing/gateways/modern_payments_cim.rb +219 -0
  95. data/lib/active_merchant/billing/gateways/moneris.rb +309 -0
  96. data/lib/active_merchant/billing/gateways/moneris_us.rb +291 -0
  97. data/lib/active_merchant/billing/gateways/money_movers.rb +152 -0
  98. data/lib/active_merchant/billing/gateways/nab_transact.rb +280 -0
  99. data/lib/active_merchant/billing/gateways/net_registry.rb +198 -0
  100. data/lib/active_merchant/billing/gateways/netaxept.rb +181 -0
  101. data/lib/active_merchant/billing/gateways/netbilling.rb +190 -0
  102. data/lib/active_merchant/billing/gateways/netpay.rb +223 -0
  103. data/lib/active_merchant/billing/gateways/network_merchants.rb +242 -0
  104. data/lib/active_merchant/billing/gateways/nmi.rb +256 -0
  105. data/lib/active_merchant/billing/gateways/ogone.rb +435 -0
  106. data/lib/active_merchant/billing/gateways/openpay.rb +194 -0
  107. data/lib/active_merchant/billing/gateways/optimal_payment.rb +313 -0
  108. data/lib/active_merchant/billing/gateways/orbital.rb +803 -0
  109. data/lib/active_merchant/billing/gateways/orbital/orbital_soft_descriptors.rb +47 -0
  110. data/lib/active_merchant/billing/gateways/pac_net_raven.rb +207 -0
  111. data/lib/active_merchant/billing/gateways/pago_facil.rb +122 -0
  112. data/lib/active_merchant/billing/gateways/pay_gate_xml.rb +261 -0
  113. data/lib/active_merchant/billing/gateways/pay_junction.rb +390 -0
  114. data/lib/active_merchant/billing/gateways/pay_secure.rb +112 -0
  115. data/lib/active_merchant/billing/gateways/pay_u_latam.rb +462 -0
  116. data/lib/active_merchant/billing/gateways/paybox_direct.rb +188 -0
  117. data/lib/active_merchant/billing/gateways/payex.rb +412 -0
  118. data/lib/active_merchant/billing/gateways/payflow.rb +304 -0
  119. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +209 -0
  120. data/lib/active_merchant/billing/gateways/payflow/payflow_express_response.rb +39 -0
  121. data/lib/active_merchant/billing/gateways/payflow/payflow_response.rb +13 -0
  122. data/lib/active_merchant/billing/gateways/payflow_express.rb +224 -0
  123. data/lib/active_merchant/billing/gateways/payflow_express_uk.rb +15 -0
  124. data/lib/active_merchant/billing/gateways/payflow_uk.rb +21 -0
  125. data/lib/active_merchant/billing/gateways/payment_express.rb +353 -0
  126. data/lib/active_merchant/billing/gateways/paymill.rb +281 -0
  127. data/lib/active_merchant/billing/gateways/paypal.rb +117 -0
  128. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +670 -0
  129. data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +65 -0
  130. data/lib/active_merchant/billing/gateways/paypal/paypal_recurring_api.rb +262 -0
  131. data/lib/active_merchant/billing/gateways/paypal_ca.rb +13 -0
  132. data/lib/active_merchant/billing/gateways/paypal_digital_goods.rb +44 -0
  133. data/lib/active_merchant/billing/gateways/paypal_express.rb +264 -0
  134. data/lib/active_merchant/billing/gateways/paypal_express_common.rb +30 -0
  135. data/lib/active_merchant/billing/gateways/payscout.rb +162 -0
  136. data/lib/active_merchant/billing/gateways/paystation.rb +199 -0
  137. data/lib/active_merchant/billing/gateways/payway.rb +207 -0
  138. data/lib/active_merchant/billing/gateways/pin.rb +197 -0
  139. data/lib/active_merchant/billing/gateways/plugnpay.rb +283 -0
  140. data/lib/active_merchant/billing/gateways/psigate.rb +216 -0
  141. data/lib/active_merchant/billing/gateways/psl_card.rb +303 -0
  142. data/lib/active_merchant/billing/gateways/qbms.rb +292 -0
  143. data/lib/active_merchant/billing/gateways/quantum.rb +276 -0
  144. data/lib/active_merchant/billing/gateways/quickpay.rb +367 -0
  145. data/lib/active_merchant/billing/gateways/realex.rb +298 -0
  146. data/lib/active_merchant/billing/gateways/redsys.rb +391 -0
  147. data/lib/active_merchant/billing/gateways/sage.rb +175 -0
  148. data/lib/active_merchant/billing/gateways/sage/sage_bankcard.rb +87 -0
  149. data/lib/active_merchant/billing/gateways/sage/sage_core.rb +114 -0
  150. data/lib/active_merchant/billing/gateways/sage/sage_vault.rb +149 -0
  151. data/lib/active_merchant/billing/gateways/sage/sage_virtual_check.rb +102 -0
  152. data/lib/active_merchant/billing/gateways/sage_pay.rb +398 -0
  153. data/lib/active_merchant/billing/gateways/sallie_mae.rb +143 -0
  154. data/lib/active_merchant/billing/gateways/secure_net.rb +252 -0
  155. data/lib/active_merchant/billing/gateways/secure_pay.rb +201 -0
  156. data/lib/active_merchant/billing/gateways/secure_pay_au.rb +281 -0
  157. data/lib/active_merchant/billing/gateways/secure_pay_tech.rb +105 -0
  158. data/lib/active_merchant/billing/gateways/skip_jack.rb +452 -0
  159. data/lib/active_merchant/billing/gateways/smart_ps.rb +283 -0
  160. data/lib/active_merchant/billing/gateways/so_easy_pay.rb +194 -0
  161. data/lib/active_merchant/billing/gateways/spreedly_core.rb +247 -0
  162. data/lib/active_merchant/billing/gateways/stripe.rb +411 -0
  163. data/lib/active_merchant/billing/gateways/swipe_checkout.rb +157 -0
  164. data/lib/active_merchant/billing/gateways/tns.rb +227 -0
  165. data/lib/active_merchant/billing/gateways/trans_first.rb +126 -0
  166. data/lib/active_merchant/billing/gateways/transax.rb +23 -0
  167. data/lib/active_merchant/billing/gateways/transnational.rb +10 -0
  168. data/lib/active_merchant/billing/gateways/trust_commerce.rb +416 -0
  169. data/lib/active_merchant/billing/gateways/usa_epay.rb +25 -0
  170. data/lib/active_merchant/billing/gateways/usa_epay_advanced.rb +1516 -0
  171. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +254 -0
  172. data/lib/active_merchant/billing/gateways/verifi.rb +225 -0
  173. data/lib/active_merchant/billing/gateways/viaklix.rb +183 -0
  174. data/lib/active_merchant/billing/gateways/vindicia.rb +385 -0
  175. data/lib/active_merchant/billing/gateways/webpay.rb +97 -0
  176. data/lib/active_merchant/billing/gateways/wepay.rb +189 -0
  177. data/lib/active_merchant/billing/gateways/wirecard.rb +421 -0
  178. data/lib/active_merchant/billing/gateways/worldpay.rb +331 -0
  179. data/lib/active_merchant/billing/gateways/worldpay_us.rb +181 -0
  180. data/lib/active_merchant/billing/model.rb +30 -0
  181. data/lib/active_merchant/billing/payment_token.rb +21 -0
  182. data/lib/active_merchant/billing/rails.rb +3 -0
  183. data/lib/active_merchant/billing/response.rb +91 -0
  184. data/lib/active_merchant/country.rb +332 -0
  185. data/lib/active_merchant/empty.rb +20 -0
  186. data/lib/active_merchant/errors.rb +29 -0
  187. data/lib/active_merchant/offsite_payments_shim.rb +19 -0
  188. data/lib/active_merchant/version.rb +3 -0
  189. data/lib/activemerchant.rb +1 -0
  190. data/lib/support/gateway_support.rb +71 -0
  191. data/lib/support/outbound_hosts.rb +25 -0
  192. data/lib/support/ssl_verify.rb +93 -0
  193. metadata +400 -0
@@ -0,0 +1,100 @@
1
+ module ActiveMerchant
2
+ module Billing
3
+ module MigsCodes
4
+ TXN_RESPONSE_CODES = {
5
+ '?' => 'Response Unknown',
6
+ '0' => 'Transaction Successful',
7
+ '1' => 'Transaction Declined - Bank Error',
8
+ '2' => 'Bank Declined Transaction',
9
+ '3' => 'Transaction Declined - No Reply from Bank',
10
+ '4' => 'Transaction Declined - Expired Card',
11
+ '5' => 'Transaction Declined - Insufficient funds',
12
+ '6' => 'Transaction Declined - Error Communicating with Bank',
13
+ '7' => 'Payment Server Processing Error - Typically caused by invalid input data such as an invalid credit card number. Processing errors can also occur',
14
+ '8' => 'Transaction Declined - Transaction Type Not Supported',
15
+ '9' => 'Bank Declined Transaction (Do not contact Bank)',
16
+ 'A' => 'Transaction Aborted',
17
+ 'C' => 'Transaction Cancelled',
18
+ 'D' => 'Deferred Transaction',
19
+ 'E' => 'Issuer Returned a Referral Response',
20
+ 'F' => '3D Secure Authentication Failed',
21
+ 'I' => 'Card Security Code Failed',
22
+ 'L' => 'Shopping Transaction Locked (This indicates that there is another transaction taking place using the same shopping transaction number)',
23
+ 'N' => 'Cardholder is not enrolled in 3D Secure (Authentication Only)',
24
+ 'P' => 'Transaction is Pending',
25
+ 'R' => 'Retry Limits Exceeded, Transaction Not Processed',
26
+ 'S' => 'Duplicate OrderInfo used. (This is only relevant for Payment Servers that enforce the uniqueness of this field)',
27
+ 'U' => 'Card Security Code Failed'
28
+ }
29
+
30
+ ISSUER_RESPONSE_CODES = {
31
+ '00' => 'Approved',
32
+ '01' => 'Refer to Card Issuer',
33
+ '02' => 'Refer to Card Issuer',
34
+ '03' => 'Invalid Merchant',
35
+ '04' => 'Pick Up Card',
36
+ '05' => 'Do Not Honor',
37
+ '07' => 'Pick Up Card',
38
+ '12' => 'Invalid Transaction',
39
+ '14' => 'Invalid Card Number (No such Number)',
40
+ '15' => 'No Such Issuer',
41
+ '33' => 'Expired Card',
42
+ '34' => 'Suspected Fraud',
43
+ '36' => 'Restricted Card',
44
+ '39' => 'No Credit Account',
45
+ '41' => 'Card Reported Lost',
46
+ '43' => 'Stolen Card',
47
+ '51' => 'Insufficient Funds',
48
+ '54' => 'Expired Card',
49
+ '57' => 'Transaction Not Permitted',
50
+ '59' => 'Suspected Fraud',
51
+ '62' => 'Restricted Card',
52
+ '65' => 'Exceeds withdrawal frequency limit',
53
+ '91' => 'Cannot Contact Issuer'
54
+ }
55
+
56
+ VERIFIED_3D_CODES = {
57
+ 'Y' => 'The cardholder was successfully authenticated.',
58
+ 'E' => 'The cardholder is not enrolled.',
59
+ 'N' => 'The cardholder was not verified.',
60
+ 'U' => 'The cardholder\'s Issuer was unable to authenticate due to a system error at the Issuer.',
61
+ 'F' => 'An error exists in the format of the request from the merchant. For example, the request did not contain all required fields, or the format of some fields was invalid.',
62
+ 'A' => 'Authentication of your Merchant ID and Password to the Directory Server Failed (see "What does a Payment Authentication Status of "A" mean?" on page 85).',
63
+ 'D' => 'Error communicating with the Directory Server, for example, the Payment Server could not connect to the directory server or there was a versioning mismatch.',
64
+ 'C' => 'The card type is not supported for authentication.',
65
+ 'M' => 'This indicates that attempts processing was used. Verification is marked with status M - ACS attempts processing used. Payment is performed with authentication. Attempts is when a cardholder has successfully passed the directory server but decides not to continue with the authentication process and cancels.',
66
+ 'S' => 'The signature on the response received from the Issuer could not be validated. This should be considered a failure.',
67
+ 'T' => 'ACS timed out. The Issuer\'s ACS did not respond to the Authentication request within the time out period.',
68
+ 'P' => 'Error parsing input from Issuer.',
69
+ 'I' => 'Internal Payment Server system error. This could be caused by a temporary DB failure or an error in the security module or by some error in an internal system.'
70
+ }
71
+
72
+ class CreditCardType
73
+ attr_accessor :am_code, :migs_code, :migs_long_code, :name
74
+ def initialize(am_code, migs_code, migs_long_code, name)
75
+ @am_code = am_code
76
+ @migs_code = migs_code
77
+ @migs_long_code = migs_long_code
78
+ @name = name
79
+ end
80
+ end
81
+
82
+ CARD_TYPES = [
83
+ # The following are 4 different representations of credit card types
84
+ # am_code: The active merchant code
85
+ # migs_code: Used in response for purchase/authorize/status
86
+ # migs_long_code: Used to pre-select card for server_purchase_url
87
+ # name: The nice display name
88
+ %w(american_express AE Amex American\ Express),
89
+ %w(diners_club DC Dinersclub Diners\ Club),
90
+ %w(jcb JC JCB JCB\ Card),
91
+ %w(maestro MS Maestro Maestro\ Card),
92
+ %w(master MC Mastercard MasterCard),
93
+ %w(na PL PrivateLabelCard Private\ Label\ Card),
94
+ %w(visa VC Visa Visa\ Card')
95
+ ].map do |am_code, migs_code, migs_long_code, name|
96
+ CreditCardType.new(am_code, migs_code, migs_long_code, name)
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,37 @@
1
+ require File.dirname(__FILE__) + '/modern_payments_cim'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+ class ModernPaymentsGateway < Gateway
6
+ self.supported_countries = ModernPaymentsCimGateway.supported_countries
7
+ self.supported_cardtypes = ModernPaymentsCimGateway.supported_cardtypes
8
+ self.homepage_url = ModernPaymentsCimGateway.homepage_url
9
+ self.display_name = ModernPaymentsCimGateway.display_name
10
+
11
+ self.abstract_class = true
12
+
13
+ def initialize(options = {})
14
+ requires!(options, :login, :password)
15
+ super
16
+ end
17
+
18
+ def purchase(money, credit_card, options = {})
19
+ customer_response = cim.create_customer(options)
20
+ return customer_response unless customer_response.success?
21
+
22
+ customer_id = customer_response.params["create_customer_result"]
23
+
24
+ card_response = cim.modify_customer_credit_card(customer_id, credit_card)
25
+ return card_response unless card_response.success?
26
+
27
+ cim.authorize_credit_card_payment(customer_id, money)
28
+ end
29
+
30
+ private
31
+ def cim
32
+ @cim ||= ModernPaymentsCimGateway.new(@options)
33
+ end
34
+ end
35
+ end
36
+ end
37
+
@@ -0,0 +1,219 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class ModernPaymentsCimGateway < Gateway #:nodoc:
4
+ self.test_url = "https://secure.modpay.com/netservices/test/ModpayTest.asmx"
5
+ self.live_url = 'https://secure.modpay.com/ws/modpay.asmx'
6
+
7
+ LIVE_XMLNS = "https://secure.modpay.com/ws/"
8
+ TEST_XMLNS = "https://secure.modpay.com/netservices/test/"
9
+
10
+ self.supported_countries = ['US']
11
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover]
12
+ self.homepage_url = 'http://www.modpay.com'
13
+ self.display_name = 'Modern Payments'
14
+
15
+ SUCCESS_MESSAGE = "Transaction accepted"
16
+ FAILURE_MESSAGE = "Transaction failed"
17
+ ERROR_MESSAGE = "Transaction error"
18
+
19
+ PAYMENT_METHOD = {
20
+ :check => 1,
21
+ :credit_card => 2
22
+ }
23
+
24
+ def initialize(options = {})
25
+ requires!(options, :login, :password)
26
+ super
27
+ end
28
+
29
+ def create_customer(options = {})
30
+ post = {}
31
+ add_customer_data(post, options)
32
+ add_address(post, options)
33
+
34
+ commit('CreateCustomer', post)
35
+ end
36
+
37
+ def modify_customer_credit_card(customer_id, credit_card)
38
+ raise ArgumentError, "The customer_id cannot be blank" if customer_id.blank?
39
+
40
+ post = {}
41
+ add_customer_id(post, customer_id)
42
+ add_credit_card(post, credit_card)
43
+
44
+ commit('ModifyCustomerCreditCard', post)
45
+ end
46
+
47
+ def authorize_credit_card_payment(customer_id, amount)
48
+ raise ArgumentError, "The customer_id cannot be blank" if customer_id.blank?
49
+
50
+ post = {}
51
+ add_customer_id(post, customer_id)
52
+ add_amount(post, amount)
53
+
54
+ commit('AuthorizeCreditCardPayment', post)
55
+ end
56
+
57
+ def create_payment(customer_id, amount, options = {})
58
+ raise ArgumentError, "The customer_id cannot be blank" if customer_id.blank?
59
+
60
+ post = {}
61
+ add_customer_id(post, customer_id)
62
+ add_amount(post, amount)
63
+ add_payment_details(post, options)
64
+
65
+ commit('CreatePayment', post)
66
+ end
67
+
68
+ private
69
+ def add_payment_details(post, options)
70
+ post[:pmtDate] = (options[:payment_date] || Time.now.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
71
+ post[:pmtType] = PAYMENT_METHOD[options[:payment_method] || :credit_card]
72
+ end
73
+
74
+ def add_amount(post, money)
75
+ post[:pmtAmount] = amount(money)
76
+ end
77
+
78
+ def add_customer_id(post, customer_id)
79
+ post[:custId] = customer_id
80
+ end
81
+
82
+ def add_customer_data(post, options)
83
+ post[:acctNum] = options[:customer]
84
+ end
85
+
86
+ def add_address(post, options)
87
+ address = options[:billing_address] || options[:address] || {}
88
+
89
+ if name = address[:name]
90
+ segments = name.split(' ')
91
+ post[:lastName] = segments.pop
92
+ post[:firstName] = segments.join(' ')
93
+ else
94
+ post[:firstName] = address[:first_name]
95
+ post[:lastName] = address[:last_name]
96
+ end
97
+
98
+ post[:address] = address[:address1]
99
+ post[:city] = address[:city]
100
+ post[:state] = address[:state]
101
+ post[:zip] = address[:zip]
102
+ post[:phone] = address[:phone]
103
+ post[:fax] = address[:fax]
104
+ post[:email] = options[:email]
105
+ end
106
+
107
+ def add_credit_card(post, credit_card)
108
+ post[:ccName] = credit_card.name
109
+ post[:ccNum] = credit_card.number
110
+ post[:expMonth] = credit_card.month
111
+ post[:expYear] = credit_card.year
112
+ end
113
+
114
+ def build_request(action, params)
115
+ xml = Builder::XmlMarkup.new :indent => 2
116
+ xml.instruct!
117
+ xml.tag! 'env:Envelope',
118
+ { 'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema',
119
+ 'xmlns:env' => 'http://schemas.xmlsoap.org/soap/envelope/',
120
+ 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance' } do
121
+
122
+ xml.tag! 'env:Body' do
123
+ xml.tag! action, { "xmlns" => xmlns(action) } do
124
+ xml.tag! "clientId", @options[:login]
125
+ xml.tag! "clientCode", @options[:password]
126
+ params.each {|key, value| xml.tag! key, value }
127
+ end
128
+ end
129
+ end
130
+ xml.target!
131
+ end
132
+
133
+ def xmlns(action)
134
+ if test? && action == 'AuthorizeCreditCardPayment'
135
+ TEST_XMLNS
136
+ else
137
+ LIVE_XMLNS
138
+ end
139
+ end
140
+
141
+ def url(action)
142
+ if test? && action == 'AuthorizeCreditCardPayment'
143
+ self.test_url
144
+ else
145
+ self.live_url
146
+ end
147
+ end
148
+
149
+ def commit(action, params)
150
+ data = ssl_post(url(action), build_request(action, params),
151
+ { 'Content-Type' =>'text/xml; charset=utf-8',
152
+ 'SOAPAction' => "#{xmlns(action)}#{action}" }
153
+ )
154
+
155
+ response = parse(action, data)
156
+ Response.new(successful?(action, response), message_from(action, response), response,
157
+ :test => test?,
158
+ :authorization => authorization_from(action, response),
159
+ :avs_result => { :code => response[:avs_code] }
160
+ )
161
+ end
162
+
163
+ def authorization_from(action, response)
164
+ response[authorization_key(action)]
165
+ end
166
+
167
+ def authorization_key(action)
168
+ action == "AuthorizeCreditCardPayment" ? :trans_id : "#{action.underscore}_result".to_sym
169
+ end
170
+
171
+ def successful?(action, response)
172
+ key = authorization_key(action)
173
+
174
+ if key == :trans_id
175
+ response[:approved] == "true"
176
+ else
177
+ response[key].to_i > 0
178
+ end
179
+ end
180
+
181
+ def message_from(action, response)
182
+ if response[:faultcode]
183
+ ERROR_MESSAGE
184
+ elsif successful?(action, response)
185
+ SUCCESS_MESSAGE
186
+ else
187
+ FAILURE_MESSAGE
188
+ end
189
+ end
190
+
191
+ def parse(action, xml)
192
+ response = {}
193
+ response[:action] = action
194
+
195
+ xml = REXML::Document.new(xml)
196
+ if root = REXML::XPath.first(xml, "//#{action}Response")
197
+ root.elements.to_a.each do |node|
198
+ parse_element(response, node)
199
+ end
200
+ elsif root = REXML::XPath.first(xml, "//soap:Fault")
201
+ root.elements.to_a.each do |node|
202
+ response[node.name.underscore.to_sym] = node.text
203
+ end
204
+ end
205
+
206
+ response
207
+ end
208
+
209
+ def parse_element(response, node)
210
+ if node.has_elements?
211
+ node.elements.each{|e| parse_element(response, e) }
212
+ else
213
+ response[node.name.underscore.to_sym] = node.text.to_s.strip
214
+ end
215
+ end
216
+ end
217
+ end
218
+ end
219
+
@@ -0,0 +1,309 @@
1
+ require 'rexml/document'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+
6
+ # To learn more about the Moneris gateway, please contact
7
+ # eselectplus@moneris.com for a copy of their integration guide. For
8
+ # information on remote testing, please see "Test Environment Penny Value
9
+ # Response Table", and "Test Environment eFraud (AVS and CVD) Penny
10
+ # Response Values", available at Moneris' {eSelect Plus Documentation
11
+ # Centre}[https://www3.moneris.com/connect/en/documents/index.html].
12
+ class MonerisGateway < Gateway
13
+ self.test_url = 'https://esqa.moneris.com/gateway2/servlet/MpgRequest'
14
+ self.live_url = 'https://www3.moneris.com/gateway2/servlet/MpgRequest'
15
+
16
+ self.supported_countries = ['CA']
17
+ self.supported_cardtypes = [:visa, :master, :american_express, :diners_club, :discover]
18
+ self.homepage_url = 'http://www.moneris.com/'
19
+ self.display_name = 'Moneris'
20
+
21
+ # Initialize the Gateway
22
+ #
23
+ # The gateway requires that a valid login and password be passed
24
+ # in the +options+ hash.
25
+ #
26
+ # ==== Options
27
+ #
28
+ # * <tt>:login</tt> -- Your Store ID
29
+ # * <tt>:password</tt> -- Your API Token
30
+ # * <tt>:cvv_enabled</tt> -- Specify that you would like the CVV passed to the gateway.
31
+ # Only particular account types at Moneris will allow this.
32
+ # Defaults to false. (optional)
33
+ def initialize(options = {})
34
+ requires!(options, :login, :password)
35
+ @cvv_enabled = options[:cvv_enabled]
36
+ @avs_enabled = options[:avs_enabled]
37
+ options = { :crypt_type => 7 }.merge(options)
38
+ super
39
+ end
40
+
41
+ # Referred to as "PreAuth" in the Moneris integration guide, this action
42
+ # verifies and locks funds on a customer's card, which then must be
43
+ # captured at a later date.
44
+ #
45
+ # Pass in +order_id+ and optionally a +customer+ parameter.
46
+ def authorize(money, creditcard_or_datakey, options = {})
47
+ requires!(options, :order_id)
48
+ post = {}
49
+ add_payment_source(post, creditcard_or_datakey, options)
50
+ post[:amount] = amount(money)
51
+ post[:order_id] = options[:order_id]
52
+ post[:address] = options[:billing_address] || options[:address]
53
+ post[:crypt_type] = options[:crypt_type] || @options[:crypt_type]
54
+ action = (post[:data_key].blank?) ? 'preauth' : 'res_preauth_cc'
55
+ commit(action, post)
56
+ end
57
+
58
+ # This action verifies funding on a customer's card and readies them for
59
+ # deposit in a merchant's account.
60
+ #
61
+ # Pass in <tt>order_id</tt> and optionally a <tt>customer</tt> parameter
62
+ def purchase(money, creditcard_or_datakey, options = {})
63
+ requires!(options, :order_id)
64
+ post = {}
65
+ add_payment_source(post, creditcard_or_datakey, options)
66
+ post[:amount] = amount(money)
67
+ post[:order_id] = options[:order_id]
68
+ post[:address] = options[:billing_address] || options[:address]
69
+ post[:crypt_type] = options[:crypt_type] || @options[:crypt_type]
70
+ action = (post[:data_key].blank?) ? 'purchase' : 'res_purchase_cc'
71
+ commit(action, post)
72
+ end
73
+
74
+ # This method retrieves locked funds from a customer's account (from a
75
+ # PreAuth) and prepares them for deposit in a merchant's account.
76
+ #
77
+ # Note: Moneris requires both the order_id and the transaction number of
78
+ # the original authorization. To maintain the same interface as the other
79
+ # gateways the two numbers are concatenated together with a ; separator as
80
+ # the authorization number returned by authorization
81
+ def capture(money, authorization, options = {})
82
+ commit 'completion', crediting_params(authorization, :comp_amount => amount(money))
83
+ end
84
+
85
+ # Voiding requires the original transaction ID and order ID of some open
86
+ # transaction. Closed transactions must be refunded.
87
+ #
88
+ # Moneris supports the voiding of an unsettled capture or purchase via
89
+ # its <tt>purchasecorrection</tt> command. This action can only occur
90
+ # on the same day as the capture/purchase prior to 22:00-23:00 EST. If
91
+ # you want to do this, pass <tt>:purchasecorrection => true</tt> as
92
+ # an option.
93
+ #
94
+ # Fun, Historical Trivia:
95
+ # Voiding an authorization in Moneris is a relatively new feature
96
+ # (September, 2011). It is actually done by doing a $0 capture.
97
+ #
98
+ # Concatenate your transaction number and order_id by using a semicolon
99
+ # (';'). This is to keep the Moneris interface consistent with other
100
+ # gateways. (See +capture+ for details.)
101
+ def void(authorization, options = {})
102
+ if options[:purchasecorrection]
103
+ commit 'purchasecorrection', crediting_params(authorization)
104
+ else
105
+ capture(0, authorization, options)
106
+ end
107
+ end
108
+
109
+ # Performs a refund. This method requires that the original transaction
110
+ # number and order number be included. Concatenate your transaction
111
+ # number and order_id by using a semicolon (';'). This is to keep the
112
+ # Moneris interface consistent with other gateways. (See +capture+ for
113
+ # details.)
114
+ def credit(money, authorization, options = {})
115
+ ActiveMerchant.deprecated CREDIT_DEPRECATION_MESSAGE
116
+ refund(money, authorization, options)
117
+ end
118
+
119
+ def refund(money, authorization, options = {})
120
+ commit 'refund', crediting_params(authorization, :amount => amount(money))
121
+ end
122
+
123
+ def store(credit_card, options = {})
124
+ post = {}
125
+ post[:pan] = credit_card.number
126
+ post[:expdate] = expdate(credit_card)
127
+ post[:crypt_type] = options[:crypt_type] || @options[:crypt_type]
128
+ commit('res_add_cc', post)
129
+ end
130
+
131
+ def unstore(data_key, options = {})
132
+ post = {}
133
+ post[:data_key] = data_key
134
+ commit('res_delete', post)
135
+ end
136
+
137
+ def update(data_key, credit_card, options = {})
138
+ post = {}
139
+ post[:pan] = credit_card.number
140
+ post[:expdate] = expdate(credit_card)
141
+ post[:data_key] = data_key
142
+ post[:crypt_type] = options[:crypt_type] || @options[:crypt_type]
143
+ commit('res_update_cc', post)
144
+ end
145
+
146
+ private # :nodoc: all
147
+
148
+ def expdate(creditcard)
149
+ sprintf("%.4i", creditcard.year)[-2..-1] + sprintf("%.2i", creditcard.month)
150
+ end
151
+
152
+ def add_payment_source(post, source, options)
153
+ if source.is_a?(String)
154
+ post[:data_key] = source
155
+ post[:cust_id] = options[:customer]
156
+ else
157
+ if source.respond_to?(:track_data) && source.track_data.present?
158
+ post[:pos_code] = '00'
159
+ post[:track2] = source.track_data
160
+ else
161
+ post[:pan] = source.number
162
+ post[:expdate] = expdate(source)
163
+ post[:cvd_value] = source.verification_value if source.verification_value?
164
+ end
165
+ post[:cust_id] = options[:customer] || source.name
166
+ end
167
+ end
168
+
169
+ # Common params used amongst the +credit+, +void+ and +capture+ methods
170
+ def crediting_params(authorization, options = {})
171
+ {
172
+ :txn_number => split_authorization(authorization).first,
173
+ :order_id => split_authorization(authorization).last,
174
+ :crypt_type => options[:crypt_type] || @options[:crypt_type]
175
+ }.merge(options)
176
+ end
177
+
178
+ # Splits an +authorization+ param and retrieves the order id and
179
+ # transaction number in that order.
180
+ def split_authorization(authorization)
181
+ if authorization.nil? || authorization.empty? || authorization !~ /;/
182
+ raise ArgumentError, 'You must include a valid authorization code (e.g. "1234;567")'
183
+ else
184
+ authorization.split(';')
185
+ end
186
+ end
187
+
188
+ def commit(action, parameters = {})
189
+ data = post_data(action, parameters)
190
+ url = test? ? self.test_url : self.live_url
191
+ raw = ssl_post(url, data)
192
+ response = parse(raw)
193
+
194
+ Response.new(successful?(response), message_from(response[:message]), response,
195
+ :test => test?,
196
+ :avs_result => { :code => response[:avs_result_code] },
197
+ :cvv_result => response[:cvd_result_code] && response[:cvd_result_code][-1,1],
198
+ :authorization => authorization_from(response)
199
+ )
200
+ end
201
+
202
+ # Generates a Moneris authorization string of the form 'trans_id;receipt_id'.
203
+ def authorization_from(response = {})
204
+ if response[:trans_id] && response[:receipt_id]
205
+ "#{response[:trans_id]};#{response[:receipt_id]}"
206
+ end
207
+ end
208
+
209
+ # Tests for a successful response from Moneris' servers
210
+ def successful?(response)
211
+ response[:response_code] &&
212
+ response[:complete] &&
213
+ (0..49).include?(response[:response_code].to_i)
214
+ end
215
+
216
+ def parse(xml)
217
+ response = { :message => "Global Error Receipt", :complete => false }
218
+ hashify_xml!(xml, response)
219
+ response
220
+ end
221
+
222
+ def hashify_xml!(xml, response)
223
+ xml = REXML::Document.new(xml)
224
+ return if xml.root.nil?
225
+ xml.elements.each('//receipt/*') do |node|
226
+ response[node.name.underscore.to_sym] = normalize(node.text)
227
+ end
228
+ end
229
+
230
+ def post_data(action, parameters = {})
231
+ xml = REXML::Document.new
232
+ root = xml.add_element("request")
233
+ root.add_element("store_id").text = options[:login]
234
+ root.add_element("api_token").text = options[:password]
235
+ root.add_element(transaction_element(action, parameters))
236
+
237
+ xml.to_s
238
+ end
239
+
240
+ def transaction_element(action, parameters)
241
+ transaction = REXML::Element.new(action)
242
+
243
+ # Must add the elements in the correct order
244
+ actions[action].each do |key|
245
+ case key
246
+ when :avs_info
247
+ transaction.add_element(avs_element(parameters[:address])) if @avs_enabled && parameters[:address]
248
+ when :cvd_info
249
+ transaction.add_element(cvd_element(parameters[:cvd_value])) if @cvv_enabled
250
+ else
251
+ transaction.add_element(key.to_s).text = parameters[key] unless parameters[key].blank?
252
+ end
253
+ end
254
+
255
+ transaction
256
+ end
257
+
258
+ def avs_element(address)
259
+ full_address = "#{address[:address1]} #{address[:address2]}"
260
+ tokens = full_address.split(/\s+/)
261
+
262
+ element = REXML::Element.new('avs_info')
263
+ element.add_element('avs_street_number').text = tokens.select{|x| x =~ /\d/}.join(' ')
264
+ element.add_element('avs_street_name').text = tokens.reject{|x| x =~ /\d/}.join(' ')
265
+ element.add_element('avs_zipcode').text = address[:zip]
266
+ element
267
+ end
268
+
269
+ def cvd_element(cvd_value)
270
+ element = REXML::Element.new('cvd_info')
271
+ if cvd_value
272
+ element.add_element('cvd_indicator').text = "1"
273
+ element.add_element('cvd_value').text = cvd_value
274
+ else
275
+ element.add_element('cvd_indicator').text = "0"
276
+ end
277
+ element
278
+ end
279
+
280
+ def message_from(message)
281
+ return 'Unspecified error' if message.blank?
282
+ message.gsub(/[^\w]/, ' ').split.join(" ").capitalize
283
+ end
284
+
285
+ def actions
286
+ {
287
+ "purchase" => [:order_id, :cust_id, :amount, :pan, :expdate, :crypt_type, :avs_info, :cvd_info, :track2, :pos_code],
288
+ "preauth" => [:order_id, :cust_id, :amount, :pan, :expdate, :crypt_type, :avs_info, :cvd_info, :track2, :pos_code],
289
+ "command" => [:order_id],
290
+ "refund" => [:order_id, :amount, :txn_number, :crypt_type],
291
+ "indrefund" => [:order_id, :cust_id, :amount, :pan, :expdate, :crypt_type],
292
+ "completion" => [:order_id, :comp_amount, :txn_number, :crypt_type],
293
+ "purchasecorrection" => [:order_id, :txn_number, :crypt_type],
294
+ "cavvpurcha" => [:order_id, :cust_id, :amount, :pan, :expdate, :cav],
295
+ "cavvpreaut" => [:order_id, :cust_id, :amount, :pan, :expdate, :cavv],
296
+ "transact" => [:order_id, :cust_id, :amount, :pan, :expdate, :crypt_type],
297
+ "Batchcloseall" => [],
298
+ "opentotals" => [:ecr_number],
299
+ "batchclose" => [:ecr_number],
300
+ "res_add_cc" => [:pan, :expdate, :crypt_type],
301
+ "res_delete" => [:data_key],
302
+ "res_update_cc" => [:data_key, :pan, :expdate, :crypt_type],
303
+ "res_purchase_cc" => [:data_key, :order_id, :cust_id, :amount, :crypt_type],
304
+ "res_preauth_cc" => [:data_key, :order_id, :cust_id, :amount, :crypt_type]
305
+ }
306
+ end
307
+ end
308
+ end
309
+ end