activemerchant 1.121.0 → 1.123.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +86 -0
  3. data/README.md +1 -1
  4. data/lib/active_merchant/billing/check.rb +13 -16
  5. data/lib/active_merchant/billing/credit_card.rb +3 -0
  6. data/lib/active_merchant/billing/credit_card_formatting.rb +1 -0
  7. data/lib/active_merchant/billing/credit_card_methods.rb +21 -12
  8. data/lib/active_merchant/billing/gateways/adyen.rb +15 -19
  9. data/lib/active_merchant/billing/gateways/authorize_net.rb +10 -8
  10. data/lib/active_merchant/billing/gateways/blue_pay.rb +29 -0
  11. data/lib/active_merchant/billing/gateways/blue_snap.rb +2 -2
  12. data/lib/active_merchant/billing/gateways/braintree_blue.rb +6 -3
  13. data/lib/active_merchant/billing/gateways/credorax.rb +2 -1
  14. data/lib/active_merchant/billing/gateways/cyber_source.rb +30 -3
  15. data/lib/active_merchant/billing/gateways/decidir.rb +7 -1
  16. data/lib/active_merchant/billing/gateways/elavon.rb +60 -28
  17. data/lib/active_merchant/billing/gateways/element.rb +2 -0
  18. data/lib/active_merchant/billing/gateways/global_collect.rb +19 -10
  19. data/lib/active_merchant/billing/gateways/kushki.rb +23 -0
  20. data/lib/active_merchant/billing/gateways/mercado_pago.rb +3 -2
  21. data/lib/active_merchant/billing/gateways/merchant_warrior.rb +2 -0
  22. data/lib/active_merchant/billing/gateways/moka.rb +277 -0
  23. data/lib/active_merchant/billing/gateways/monei.rb +228 -144
  24. data/lib/active_merchant/billing/gateways/mundipagg.rb +14 -5
  25. data/lib/active_merchant/billing/gateways/nmi.rb +14 -9
  26. data/lib/active_merchant/billing/gateways/orbital.rb +28 -6
  27. data/lib/active_merchant/billing/gateways/pay_arc.rb +390 -0
  28. data/lib/active_merchant/billing/gateways/pay_trace.rb +404 -0
  29. data/lib/active_merchant/billing/gateways/payeezy.rb +4 -0
  30. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +1 -0
  31. data/lib/active_merchant/billing/gateways/payflow.rb +9 -0
  32. data/lib/active_merchant/billing/gateways/payment_express.rb +2 -2
  33. data/lib/active_merchant/billing/gateways/paymentez.rb +5 -0
  34. data/lib/active_merchant/billing/gateways/paysafe.rb +291 -0
  35. data/lib/active_merchant/billing/gateways/payu_latam.rb +3 -3
  36. data/lib/active_merchant/billing/gateways/redsys.rb +35 -32
  37. data/lib/active_merchant/billing/gateways/safe_charge.rb +2 -0
  38. data/lib/active_merchant/billing/gateways/spreedly_core.rb +13 -4
  39. data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +19 -1
  40. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +1 -1
  41. data/lib/active_merchant/billing/gateways/vpos.rb +49 -6
  42. data/lib/active_merchant/billing/gateways/worldpay.rb +39 -7
  43. data/lib/active_merchant/billing/three_d_secure_eci_mapper.rb +27 -0
  44. data/lib/active_merchant/billing.rb +1 -0
  45. data/lib/active_merchant/version.rb +1 -1
  46. metadata +8 -3
@@ -40,7 +40,7 @@ module ActiveMerchant #:nodoc:
40
40
  add_shipping_address(post, options)
41
41
  add_payment(post, payment, options)
42
42
  add_submerchant(post, options)
43
-
43
+ add_auth_key(post, options)
44
44
  commit('sale', post)
45
45
  end
46
46
 
@@ -52,6 +52,7 @@ module ActiveMerchant #:nodoc:
52
52
  add_payment(post, payment, options)
53
53
  add_capture_flag(post, payment)
54
54
  add_submerchant(post, options)
55
+ add_auth_key(post, options)
55
56
  commit('authonly', post)
56
57
  end
57
58
 
@@ -229,9 +230,16 @@ module ActiveMerchant #:nodoc:
229
230
  end
230
231
  end
231
232
 
232
- def headers
233
+ def add_auth_key(post, options)
234
+ if authorization_secret_key = options[:authorization_secret_key]
235
+ post[:authorization_secret_key] = authorization_secret_key
236
+ end
237
+ end
238
+
239
+ def headers(authorization_secret_key = nil)
240
+ basic_token = authorization_secret_key || @options[:api_key]
233
241
  {
234
- 'Authorization' => 'Basic ' + Base64.strict_encode64("#{@options[:api_key]}:"),
242
+ 'Authorization' => 'Basic ' + Base64.strict_encode64("#{basic_token}:"),
235
243
  'Content-Type' => 'application/json',
236
244
  'Accept' => 'application/json'
237
245
  }
@@ -259,11 +267,12 @@ module ActiveMerchant #:nodoc:
259
267
 
260
268
  def commit(action, parameters, auth = nil)
261
269
  url = url_for(action, auth)
270
+ authorization_secret_key = parameters[:authorization_secret_key] if parameters
262
271
  parameters.merge!(parameters[:payment][:credit_card].delete(:card)).delete(:payment) if action == 'store'
263
272
  response = if %w[refund void].include? action
264
- parse(ssl_request(:delete, url, post_data(parameters), headers))
273
+ parse(ssl_request(:delete, url, post_data(parameters), headers(authorization_secret_key)))
265
274
  else
266
- parse(ssl_post(url, post_data(parameters), headers))
275
+ parse(ssl_post(url, post_data(parameters), headers(authorization_secret_key)))
267
276
  end
268
277
 
269
278
  Response.new(
@@ -251,15 +251,20 @@ module ActiveMerchant #:nodoc:
251
251
  end
252
252
 
253
253
  def add_three_d_secure(post, options)
254
- return unless options[:three_d_secure]
255
-
256
- if (three_d_secure = options[:three_d_secure])
257
- post[:eci] = three_d_secure[:eci]
258
- post[:cavv] = three_d_secure[:cavv]
259
- post[:xid] = three_d_secure[:xid]
260
- post[:three_ds_version] = three_d_secure[:version]
261
- post[:directory_server_id] = three_d_secure[:ds_transaction_id]
262
- end
254
+ three_d_secure = options[:three_d_secure]
255
+ return unless three_d_secure
256
+
257
+ post[:cardholder_auth] = cardholder_auth(three_d_secure[:authentication_response_status])
258
+ post[:cavv] = three_d_secure[:cavv]
259
+ post[:xid] = three_d_secure[:xid]
260
+ post[:three_ds_version] = three_d_secure[:version]
261
+ post[:directory_server_id] = three_d_secure[:ds_transaction_id]
262
+ end
263
+
264
+ def cardholder_auth(trans_status)
265
+ return nil if trans_status.nil?
266
+
267
+ trans_status == 'Y' ? 'verified' : 'attempted'
263
268
  end
264
269
 
265
270
  def add_reference(post, authorization)
@@ -42,6 +42,7 @@ module ActiveMerchant #:nodoc:
42
42
  }
43
43
 
44
44
  SUCCESS = '0'
45
+ APPROVAL_SUCCESS = '1'
45
46
 
46
47
  APPROVED = [
47
48
  '00', # Approved
@@ -334,7 +335,9 @@ module ActiveMerchant #:nodoc:
334
335
  gsub(%r((<CardSecVal>).+(</CardSecVal>)), '\1[FILTERED]\2').
335
336
  gsub(%r((<MerchantID>).+(</MerchantID>)), '\1[FILTERED]\2').
336
337
  gsub(%r((<CustomerMerchantID>).+(</CustomerMerchantID>)), '\1[FILTERED]\2').
337
- gsub(%r((<CustomerProfileMessage>).+(</CustomerProfileMessage>)), '\1[FILTERED]\2')
338
+ gsub(%r((<CustomerProfileMessage>).+(</CustomerProfileMessage>)), '\1[FILTERED]\2').
339
+ gsub(%r((<CheckDDA>).+(</CheckDDA>)), '\1[FILTERED]\2').
340
+ gsub(%r((<BCRtNum>).+(</BCRtNum>)), '\1[FILTERED]\2')
338
341
  end
339
342
 
340
343
  private
@@ -602,8 +605,10 @@ module ActiveMerchant #:nodoc:
602
605
 
603
606
  def add_mc_program_protocol(xml, creditcard, three_d_secure)
604
607
  return unless three_d_secure && creditcard.brand == 'master'
608
+ return unless three_d_secure[:version]
605
609
 
606
- xml.tag!(:MCProgramProtocol, three_d_secure[:version]) if three_d_secure[:version]
610
+ truncated_version = three_d_secure[:version].to_s[0]
611
+ xml.tag!(:MCProgramProtocol, truncated_version)
607
612
  end
608
613
 
609
614
  def add_mc_directory_trans_id(xml, creditcard, three_d_secure)
@@ -612,12 +617,20 @@ module ActiveMerchant #:nodoc:
612
617
  xml.tag!(:MCDirectoryTransID, three_d_secure[:ds_transaction_id]) if three_d_secure[:ds_transaction_id]
613
618
  end
614
619
 
615
- def add_ucafind(xml, creditcard, three_d_secure)
620
+ def add_mc_ucafind(xml, creditcard, three_d_secure)
616
621
  return unless three_d_secure && creditcard.brand == 'master'
617
622
 
618
623
  xml.tag! :UCAFInd, '4'
619
624
  end
620
625
 
626
+ def add_mc_scarecurring(xml, creditcard, parameters, three_d_secure)
627
+ return unless parameters && parameters[:sca_recurring] && creditcard.brand == 'master'
628
+
629
+ valid_eci = three_d_secure && three_d_secure[:eci] && three_d_secure[:eci] == '7'
630
+
631
+ xml.tag!(:SCARecurringPayment, parameters[:sca_recurring]) if valid_eci
632
+ end
633
+
621
634
  def add_dpanind(xml, creditcard)
622
635
  return unless creditcard.is_a?(NetworkTokenizationCreditCard)
623
636
 
@@ -745,7 +758,7 @@ module ActiveMerchant #:nodoc:
745
758
 
746
759
  def parse(body)
747
760
  response = {}
748
- xml = REXML::Document.new(body)
761
+ xml = REXML::Document.new(strip_invalid_xml_chars(body))
749
762
  root = REXML::XPath.first(xml, '//Response') ||
750
763
  REXML::XPath.first(xml, '//ErrorResponse')
751
764
  if root
@@ -799,8 +812,10 @@ module ActiveMerchant #:nodoc:
799
812
  end
800
813
 
801
814
  def success?(response, message_type)
802
- if %i[refund void].include?(message_type)
815
+ if %i[void].include?(message_type)
803
816
  response[:proc_status] == SUCCESS
817
+ elsif %i[refund].include?(message_type)
818
+ response[:proc_status] == SUCCESS && response[:approval_status] == APPROVAL_SUCCESS
804
819
  elsif response[:customer_profile_action]
805
820
  response[:profile_proc_status] == SUCCESS
806
821
  else
@@ -884,9 +899,10 @@ module ActiveMerchant #:nodoc:
884
899
  add_card_indicators(xml, parameters)
885
900
  add_stored_credentials(xml, parameters)
886
901
  add_pymt_brand_program_code(xml, payment_source, three_d_secure)
902
+ add_mc_scarecurring(xml, payment_source, parameters, three_d_secure)
887
903
  add_mc_program_protocol(xml, payment_source, three_d_secure)
888
904
  add_mc_directory_trans_id(xml, payment_source, three_d_secure)
889
- add_ucafind(xml, payment_source, three_d_secure)
905
+ add_mc_ucafind(xml, payment_source, three_d_secure)
890
906
  end
891
907
  end
892
908
  xml.target!
@@ -985,6 +1001,12 @@ module ActiveMerchant #:nodoc:
985
1001
  options[:billing_address] || options[:address]
986
1002
  end
987
1003
 
1004
+ # Null characters are possible in some responses (namely, the respMsg field), causing XML parsing errors
1005
+ # Prevent by substituting these with a valid placeholder string
1006
+ def strip_invalid_xml_chars(xml)
1007
+ xml.gsub(/\u0000/, '[null]')
1008
+ end
1009
+
988
1010
  # The valid characters include:
989
1011
  #
990
1012
  # 1. all letters and digits
@@ -0,0 +1,390 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class PayArcGateway < Gateway
4
+ self.test_url = 'https://testapi.payarc.net/v1'
5
+ self.live_url = 'https://api.payarc.net/v1'
6
+
7
+ self.supported_countries = ['US']
8
+ self.default_currency = 'usd'
9
+ self.supported_cardtypes = %i[visa master american_express discover jcb]
10
+
11
+ self.homepage_url = 'https://www.payarc.net/'
12
+ self.display_name = 'PAYARC Gateway'
13
+
14
+ STANDARD_ERROR_CODE_MAPPING = {}
15
+ STANDARD_ACTIONS = {
16
+ token:
17
+ { end_point: 'tokens',
18
+ allowed_fields: %i[card_source card_number exp_month exp_year cvv card_holder_name
19
+ address_line1 address_line2 city state zip country] },
20
+ capture:
21
+ { end_point: 'charges',
22
+ allowed_fields: %i[amount statement_description card_id currency customer_id token_id card_source tip_amount
23
+ card_level sales_tax purchase_order supplier_reference_number customer_ref_id ship_to_zip
24
+ amex_descriptor customer_vat_number summary_commodity_code shipping_charges duty_charges
25
+ ship_from_zip destination_country_code vat_invoice order_date tax_category tax_type
26
+ tax_amount tax_rate address_line1 zip terminal_id surcharge description email receipt_phone statement_descriptor ] },
27
+ void:
28
+ { end_point: 'charges/{{chargeID}}/void',
29
+ allowed_fields: %i[reason void_description] },
30
+ refund:
31
+ { end_point: 'charges/{{charge_id}}/refunds',
32
+ allowed_fields: %i[amount reason description] },
33
+ credit:
34
+ { end_point: 'refunds/wo_reference',
35
+ allowed_fields: %i[amount charge_description statement_description terminal_id card_source card_number
36
+ exp_month exp_year cvv card_holder_name address_line1 address_line2 city state zip
37
+ country currency reason receipt_phone receipt_email ] }
38
+ }
39
+
40
+ SUCCESS_STATUS = %w[
41
+ submitted_for_settlement authorized partially_submitted_for_settlement
42
+ credit partial_refund void refunded settled
43
+ ]
44
+
45
+ FAILURE_STATUS = %w[not_processed failed_by_gateway invalid_track_data authorization_expired]
46
+
47
+ # The gateway must be configured with Bearer token.
48
+ #
49
+ # <tt>:api_key</tt> PAYARC's Bearer token must be passsed to initialise the gateway.
50
+
51
+ def initialize(options = {})
52
+ requires!(options, :api_key)
53
+ super
54
+ end
55
+
56
+ #
57
+ # Purchase API through PAYARC.
58
+ #
59
+ # <tt>:money</tt> A positive integer in cents representing how much to charge. The minimum amount is 50c USD.
60
+ #
61
+ # <tt>:creditcard</tt> <tt>CreditCard</tt> object with card details.
62
+ #
63
+ # <tt>:options</tt> Other information like address, card source etc can be passed in options
64
+ #
65
+ # ==== Options
66
+ #
67
+ # * <tt>:card_source </tt> -- Source of payment (REQUIRED) ( INTERNET, SWIPE, PHONE, MAIL, MANUAL )
68
+ # * <tt>:currency </tt> -- Three-letter ISO currency code, in lowercase (REQUIRED)
69
+ # * <tt>:card_holder_name</tt> --Name of the Card Holder (OPTIONAL)
70
+ # * <tt>:address_line1</tt> -- Set in payment method's billing address (OPTIONAL)
71
+ # * <tt>:address_line2</tt> -- Set in payment method's billing address (OPTIONAL)
72
+ # * <tt>:state </tt> -- State (OPTIONAL)
73
+ # * <tt>:country </tt> -- Country (OPTIONAL)
74
+ # * <tt>:statement_description </tt> -- An arbitrary string to be displayed on your costomer's credit card statement. This may be up to 22 characters. (OPTIONAL)
75
+ # * <tt> :card_level </tt> -- Commercial card level - "LEVEL2" OR "LEVEL3" (OPTIONAL)
76
+ # * <tt> :sales_tax </tt> -- A positive integer in cents representing sales tax. (OPTIONAL)
77
+ # * <tt> :terminal_id </tt> -- Optional terminal id. (OPTIONAL)
78
+ # * <tt> :tip_amount </tt> -- A positive integer in cents representing tip amount. (OPTIONAL)
79
+ # * <tt> :sales_tax </tt> -- Applicable for LEVEL2 or LEVEL3 Charge. A positive integer in cents representing sales tax. (REQUIRED for LEVEL2 0r LEVEL3)
80
+ # * <tt> :purchase_order </tt> -- Applicable for Level2 or Level3 Charge. The value used by the customer to identify an order. Issued by the buyer to the seller. (REQUIRED for LEVEL2 0r LEVEL3)
81
+ # * <tt> :order_date </tt> -- Applicable for Level2 Charge for AMEX card only or Level3 Charge. The date the order was processed. Format: Alphanumeric and Special Character |Min Length=0 Max Length=10|Allowed format: MM/DD/YYYY For example: 12/01/2016
82
+ # * <tt> :customer_ref_id </tt> -- Applicable for Level2 Charge for AMEX card only or Level3 Charge. The reference identifier supplied by the Commercial Card cardholder. Format: Alphanumeric and Special Character |Min Length=0 Max Length=17| a-z A-Z 0-9 Space <>
83
+ # * <tt> :ship_to_zip </tt> -- Applicable for Level2 Charge for AMEX card only or Level3 Charge. The postal code for the address to which the goods are being shipped. Format: Alphanumeric |Min Length=2 Max Length=10
84
+ # * <tt> :amex_descriptor </tt> -- Applicable for Level2 Charge for AMEX card only. The value of the Transaction Advice Addendum field, displays descriptive information about a transactions on a customer's AMEX card statement. Format: Alphanumeric and Special Character |Min Length=0 Max Length=40|a-z A-Z 0-9 Space <>
85
+ # * <tt> :supplier_reference_number </tt> -- Applicable for Level2 Charge for AMEX card only or Level3 charge. The value used by the customer to identify an order. Issued by the buyer to the seller.
86
+ # * <tt> :tax_amount </tt> -- Applicable for Level3 Charge. The tax amount. Format: Numeric|Max Length=12|Allowed characters: 0-9 .(dot) Note: If a decimal point is included, the amount reflects a dollar value. If a decimal point is not included, the amount reflects a cent value.
87
+ # * <tt> :tax_category </tt> -- Applicable for Level3 Charge. The type of tax. Formerly established through TaxCategory messages. Allowed values: SERVICE, DUTY, VAT, ALTERNATE, NATIONAL, TAX_EXEMPT
88
+ # * <tt> :customer_vat_number </tt> -- Applicable for Level3 Charge. Indicates the customer's government assigned tax identification number or the identification number assigned to their purchasing company by the tax authorities. Format: Alphanumeric and Special Character|Min Length=0 Max Length=13| a-z A-Z 0-9 Space <>
89
+ # * <tt> :summary_commodity_code </tt> -- Applicable for Level3 Charge. The international description code of the overall goods or services being supplied. Format: Alphanumeric and Special Character |Min Length=0 Max Length=4|Allowed character: a-z A-Z 0-9 Space <>
90
+ # * <tt> :shipping_charges </tt> -- Applicable for Level3 Charge. The dollar amount for shipping or freight charges applied to a product or transaction. Format: Numeric |Max Length=12|Allowed characters: 0-9 .(dot) Note: If a decimal point is included, the amount reflects a dollar value. If a decimal point is not included, the amount reflects a cent value.
91
+ # * <tt> :duty_charges </tt> -- Applicable for Level3 Charge. Indicates the total charges for any import or export duties included in the order. Format: Numeric |Max Length=12|Allowed characters: 0-9 . (dot) Note: If a decimal point is included, the amount reflects a dollar value. If a decimal point is not included, the amount reflects a cent value.
92
+ # * <tt> :ship_from_zip </tt> -- Applicable for Level3 Charge. The postal code for the address to which the goods are being shipped. Format: Alphanumeric |Min Length=2 Max Length=10
93
+ # * <tt> :destination_country_code </tt> -- Applicable for Level3 Charge. The destination country code indicator. Format: Alphanumeric.
94
+ # * <tt> :tax_type </tt> -- Applicable for Level3 Charge. The type of tax. For example, VAT, NATIONAL, Service Tax. Format: Alphanumeric and Special Character
95
+ # * <tt> :vat_invoice </tt> -- Applicable for Level3 Charge. The Value Added Tax (VAT) invoice number associated with the transaction. Format: Alphanumeric and Special Character |Min Length=0 Max Length=15|Allowed character: a-z A-Z 0-9 Space <>
96
+ # * <tt> :tax_rate </tt> -- Applicable for Level3 Charge. The type of tax rate. This field is used if taxCategory is not used. Default sale tax rate in percentage Must be between 0.1% - 22% ,Applicable only Level 2 AutoFill. Format: Decimal Number |Max Length=4|Allowed characters: 0-9 .(dot) Allowed range: 0.01 - 100
97
+ # * <tt> :email </tt> -- Customer's email address sent with payment method.
98
+
99
+ def purchase(money, creditcard, options = {})
100
+ options[:capture] = 1
101
+ MultiResponse.run do |r|
102
+ r.process { token(creditcard, options) }
103
+ r.process { charge(money, r.authorization, options) }
104
+ end
105
+ end
106
+
107
+ #
108
+ # Authorize the payment API through PAYARC.
109
+ #
110
+ # <tt>:money</tt> A positive integer in cents representing how much to charge. The minimum amount is 50c USD.
111
+ #
112
+ # <tt>:creditcard</tt> <tt>CreditCard</tt> object with card details.
113
+ #
114
+ # <tt>:options</tt> Other information like address, card source etc can be passed in options
115
+ #
116
+ # ==== Options
117
+ #
118
+ # * <tt>:card_source </tt> -- Source of payment (REQUIRED) ( INTERNET, SWIPE, PHONE, MAIL, MANUAL )
119
+ # * <tt>:currency </tt> -- Three-letter ISO currency code, in lowercase (REQUIRED)
120
+ # * <tt>:card_holder_name</tt> --Name of the Card Holder (OPTIONAL)
121
+ # * <tt>:address_line1</tt> -- Set in payment method's billing address (OPTIONAL)
122
+ # * <tt>:address_line2</tt> -- Set in payment method's billing address (OPTIONAL)
123
+ # * <tt>:state </tt> -- State (OPTIONAL)
124
+ # * <tt>:country </tt> -- Country (OPTIONAL)
125
+ # * <tt>:statement_description </tt> -- An arbitrary string to be displayed on your costomer's credit card statement. This may be up to 22 characters. (OPTIONAL)
126
+ # * <tt> :card_level </tt> -- Commercial card level - "LEVEL2" OR "LEVEL3" (OPTIONAL)
127
+ # * <tt> :sales_tax </tt> -- A positive integer in cents representing sales tax. (OPTIONAL)
128
+ # * <tt> :terminal_id </tt> -- Optional terminal id. (OPTIONAL)
129
+ # * <tt> :tip_amount </tt> -- A positive integer in cents representing tip amount. (OPTIONAL)
130
+ # * <tt> :sales_tax </tt> -- Applicable for LEVEL2 or LEVEL3 Charge. A positive integer in cents representing sales tax. (REQUIRED for LEVEL2 0r LEVEL3)
131
+ # * <tt> :purchase_order </tt> -- Applicable for Level2 or Level3 Charge. The value used by the customer to identify an order. Issued by the buyer to the seller. (REQUIRED for LEVEL2 0r LEVEL3)
132
+ # * <tt> :order_date </tt> -- Applicable for Level2 Charge for AMEX card only or Level3 Charge. The date the order was processed. Format: Alphanumeric and Special Character |Min Length=0 Max Length=10|Allowed format: MM/DD/YYYY For example: 12/01/2016
133
+ # * <tt> :customer_ref_id </tt> -- Applicable for Level2 Charge for AMEX card only or Level3 Charge. The reference identifier supplied by the Commercial Card cardholder. Format: Alphanumeric and Special Character |Min Length=0 Max Length=17| a-z A-Z 0-9 Space <>
134
+ # * <tt> :ship_to_zip </tt> -- Applicable for Level2 Charge for AMEX card only or Level3 Charge. The postal code for the address to which the goods are being shipped. Format: Alphanumeric |Min Length=2 Max Length=10
135
+ # * <tt> :amex_descriptor </tt> -- Applicable for Level2 Charge for AMEX card only. The value of the Transaction Advice Addendum field, displays descriptive information about a transactions on a customer's AMEX card statement. Format: Alphanumeric and Special Character |Min Length=0 Max Length=40|a-z A-Z 0-9 Space <>
136
+ # * <tt> :supplier_reference_number </tt> -- Applicable for Level2 Charge for AMEX card only or Level3 charge. The value used by the customer to identify an order. Issued by the buyer to the seller.
137
+ # * <tt> :tax_amount </tt> -- Applicable for Level3 Charge. The tax amount. Format: Numeric|Max Length=12|Allowed characters: 0-9 .(dot) Note: If a decimal point is included, the amount reflects a dollar value. If a decimal point is not included, the amount reflects a cent value.
138
+ # * <tt> :tax_category </tt> -- Applicable for Level3 Charge. The type of tax. Formerly established through TaxCategory messages. Allowed values: SERVICE, DUTY, VAT, ALTERNATE, NATIONAL, TAX_EXEMPT
139
+ # * <tt> :customer_vat_number </tt> -- Applicable for Level3 Charge. Indicates the customer's government assigned tax identification number or the identification number assigned to their purchasing company by the tax authorities. Format: Alphanumeric and Special Character|Min Length=0 Max Length=13| a-z A-Z 0-9 Space <>
140
+ # * <tt> :summary_commodity_code </tt> -- Applicable for Level3 Charge. The international description code of the overall goods or services being supplied. Format: Alphanumeric and Special Character |Min Length=0 Max Length=4|Allowed character: a-z A-Z 0-9 Space <>
141
+ # * <tt> :shipping_charges </tt> -- Applicable for Level3 Charge. The dollar amount for shipping or freight charges applied to a product or transaction. Format: Numeric |Max Length=12|Allowed characters: 0-9 .(dot) Note: If a decimal point is included, the amount reflects a dollar value. If a decimal point is not included, the amount reflects a cent value.
142
+ # * <tt> :duty_charges </tt> -- Applicable for Level3 Charge. Indicates the total charges for any import or export duties included in the order. Format: Numeric |Max Length=12|Allowed characters: 0-9 . (dot) Note: If a decimal point is included, the amount reflects a dollar value. If a decimal point is not included, the amount reflects a cent value.
143
+ # * <tt> :ship_from_zip </tt> -- Applicable for Level3 Charge. The postal code for the address to which the goods are being shipped. Format: Alphanumeric |Min Length=2 Max Length=10
144
+ # * <tt> :destination_country_code </tt> -- Applicable for Level3 Charge. The destination country code indicator. Format: Alphanumeric.
145
+ # * <tt> :tax_type </tt> -- Applicable for Level3 Charge. The type of tax. For example, VAT, NATIONAL, Service Tax. Format: Alphanumeric and Special Character
146
+ # * <tt> :vat_invoice </tt> -- Applicable for Level3 Charge. The Value Added Tax (VAT) invoice number associated with the transaction. Format: Alphanumeric and Special Character |Min Length=0 Max Length=15|Allowed character: a-z A-Z 0-9 Space <>
147
+ # * <tt> :tax_rate </tt> -- Applicable for Level3 Charge. The type of tax rate. This field is used if taxCategory is not used. Default sale tax rate in percentage Must be between 0.1% - 22% ,Applicable only Level 2 AutoFill. Format: Decimal Number |Max Length=4|Allowed characters: 0-9 .(dot) Allowed range: 0.01 - 100
148
+ # * <tt> :email </tt> -- Customer's email address.
149
+
150
+ def authorize(money, creditcard, options = {})
151
+ options[:capture] = '0'
152
+ MultiResponse.run do |r|
153
+ r.process { token(creditcard, options) }
154
+ r.process { charge(money, r.authorization, options) }
155
+ end
156
+ end
157
+
158
+ #
159
+ # Capture the payment of an existing, uncaptured, charge.
160
+ # This is the second half of the two-step payment flow, where first you created / authorized a charge
161
+ # with the capture option set to false.
162
+ #
163
+ # <tt>:money</tt> A positive integer in cents representing how much to charge. The minimum amount is 50c USD.
164
+ #
165
+ # <tt>:tx_reference</tt> charge_id from previously created / authorized a charge
166
+ #
167
+ # <tt>:options</tt> Other information like address, card source etc can be passed in options
168
+
169
+ def capture(money, tx_reference, options = {})
170
+ post = {}
171
+ add_money(post, money, options)
172
+ action = "#{STANDARD_ACTIONS[:capture][:end_point]}/#{tx_reference}/capture"
173
+ post = filter_gateway_fields(post, options, STANDARD_ACTIONS[:capture][:allowed_fields])
174
+ commit(action, post)
175
+ end
176
+
177
+ #
178
+ # Voids the transaction / charge.
179
+ #
180
+ # <tt>:tx_reference</tt> charge_id from previously created charge
181
+ #
182
+ # <tt>:options</tt> Other information like address, card source etc can be passed in options
183
+ #
184
+ # ==== Options
185
+ #
186
+ # * <tt> :reason </tt> -- Reason for voiding transaction (REQUIRED) ( requested_by_customer, duplicate, fraudulent, other )
187
+
188
+ def void(tx_reference, options = {})
189
+ post = {}
190
+ post['reason'] = options[:reason]
191
+ action = STANDARD_ACTIONS[:void][:end_point].gsub(/{{chargeID}}/, tx_reference)
192
+ post = filter_gateway_fields(post, options, STANDARD_ACTIONS[:void][:allowed_fields])
193
+ commit(action, post)
194
+ end
195
+
196
+ #
197
+ # Refund full / partial payment of an successful charge / capture / purchase.
198
+ #
199
+ # <tt>:money</tt> A positive integer in cents representing how much to charge. The minimum amount is 50c USD.
200
+ #
201
+ # <tt>:tx_reference</tt> charge_id from previously created / authorized a charge
202
+ #
203
+ # <tt>:options</tt> Other information like address, card source etc can be passed in options
204
+
205
+ def refund(money, tx_reference, options = {})
206
+ post = {}
207
+ add_money(post, money, options)
208
+ action = STANDARD_ACTIONS[:refund][:end_point].gsub(/{{charge_id}}/, tx_reference)
209
+ post = filter_gateway_fields(post, options, STANDARD_ACTIONS[:refund][:allowed_fields])
210
+ commit(action, post)
211
+ end
212
+
213
+ def credit(money, creditcard, options = {})
214
+ post = {}
215
+ add_money(post, money, options)
216
+ add_creditcard(post, creditcard, options)
217
+ add_address(post, options)
218
+ add_phone(post, options)
219
+ post['receipt_email'] = options[:email] if options[:email]
220
+ action = STANDARD_ACTIONS[:credit][:end_point]
221
+ post = filter_gateway_fields(post, options, STANDARD_ACTIONS[:credit][:allowed_fields])
222
+ commit(action, post)
223
+ end
224
+
225
+ #
226
+ # Verify the creditcard API through PAYARC.
227
+ #
228
+ # <tt>:creditcard</tt> <tt>CreditCard</tt> object with card details.
229
+ #
230
+ # <tt>:options</tt> Other information like address, card source etc can be passed in options
231
+ #
232
+ # ==== Options
233
+ #
234
+ # * <tt>:card_source </tt> -- Source of payment (REQUIRED) ( INTERNET, SWIPE, PHONE, MAIL, MANUAL )
235
+ # * <tt>:card_holder_name</tt> --Name of the Card Holder (OPTIONAL)
236
+ # * <tt>:address_line1</tt> -- Set in payment method's billing address (OPTIONAL)
237
+ # * <tt>:address_line2</tt> -- Set in payment method's billing address (OPTIONAL)
238
+ # * <tt>:state </tt> -- State (OPTIONAL)
239
+ # * <tt>:country </tt> -- Country (OPTIONAL)
240
+
241
+ def verify(creditcard, options = {})
242
+ token(creditcard, options)
243
+ end
244
+
245
+ #:nodoc:
246
+ def token(creditcard, options = {})
247
+ post = {}
248
+ post['authorize_card'] = 1
249
+ post['card_source'] = options[:card_source]
250
+ add_creditcard(post, creditcard, options)
251
+ add_address(post, options)
252
+ post = filter_gateway_fields(post, options, STANDARD_ACTIONS[:token][:allowed_fields])
253
+ commit(STANDARD_ACTIONS[:token][:end_point], post)
254
+ end
255
+
256
+ def supports_scrubbing? #:nodoc:
257
+ true
258
+ end
259
+
260
+ def scrub(transcript)
261
+ #:nodoc:
262
+ transcript.
263
+ gsub(%r((Authorization: Bearer )[^\s]+\s)i, '\1[FILTERED]\2').
264
+ gsub(%r((&?card_number=)[^&]*)i, '\1[FILTERED]').
265
+ gsub(%r((&?cvv=)[^&]*)i, '\1[BLANK]')
266
+ end
267
+
268
+ private
269
+
270
+ def charge(money, authorization, options = {})
271
+ post = {}
272
+ post['token_id'] = authorization
273
+ post['capture'] = options[:capture] || 1
274
+ add_money(post, money, options)
275
+ add_phone(post, options)
276
+ post = filter_gateway_fields(post, options, STANDARD_ACTIONS[:capture][:allowed_fields])
277
+ commit(STANDARD_ACTIONS[:capture][:end_point], post)
278
+ end
279
+
280
+ def add_creditcard(post, creditcard, options)
281
+ post['card_number'] = creditcard.number
282
+ post['exp_month'] = format(creditcard.month, :two_digits)
283
+ post['exp_year'] = creditcard.year
284
+ post['cvv'] = creditcard.verification_value unless creditcard.verification_value.nil?
285
+ post['card_holder_name'] = options[:card_holder_name] || "#{creditcard.first_name} #{creditcard.last_name}"
286
+ end
287
+
288
+ def add_address(post, options)
289
+ post['address_line1'] = options[:billing_address][:address1]
290
+ post['address_line2'] = options[:billing_address][:address2]
291
+ post['city'] = options[:billing_address][:city]
292
+ post['state'] = options[:billing_address][:state]
293
+ post['zip'] = options[:billing_address][:zip]
294
+ post['country'] = options[:billing_address][:country]
295
+ end
296
+
297
+ def add_phone(post, options)
298
+ post['receipt_phone'] = options[:billing_address][:phone] if options[:billing_address][:phone]
299
+ end
300
+
301
+ def add_money(post, money, options)
302
+ post['amount'] = money
303
+ post['currency'] = currency(money) unless options[:currency]
304
+ post['statement_description'] = options[:statement_description]
305
+ end
306
+
307
+ def headers(api_key)
308
+ {
309
+ 'Authorization' => 'Bearer ' + api_key.strip,
310
+ 'Accept' => 'application/json',
311
+ 'User-Agent' => "PayArc ActiveMerchantBindings/#{ActiveMerchant::VERSION}"
312
+ }
313
+ end
314
+
315
+ def parse(body)
316
+ JSON.parse(body)
317
+ rescue JSON::ParserError
318
+ body
319
+ end
320
+
321
+ def filter_gateway_fields(post, options, gateway_fields)
322
+ filtered_options = options.slice(*gateway_fields).compact
323
+ post.update(filtered_options)
324
+ post
325
+ end
326
+
327
+ def commit(action, parameters)
328
+ url = (test? ? test_url : live_url)
329
+ headers = headers(@options[:api_key])
330
+ end_point = "#{url}/#{action}"
331
+ begin
332
+ response = ssl_post(end_point, post_data(parameters), headers)
333
+ parsed_response = parse(response)
334
+
335
+ Response.new(
336
+ success_from(parsed_response, action),
337
+ message_from(parsed_response, action),
338
+ parsed_response,
339
+ test: test?,
340
+ authorization: parse_response_id(parsed_response),
341
+ error_code: error_code_from(parsed_response, action)
342
+ )
343
+ rescue ResponseError => e
344
+ parsed_response = parse(e.response.body)
345
+ Response.new(
346
+ false,
347
+ message_from(parsed_response, action),
348
+ parsed_response,
349
+ test: test?,
350
+ authorization: nil,
351
+ error_code: error_code_from(parsed_response, action)
352
+ )
353
+ end
354
+ end
355
+
356
+ def success_from(response, action)
357
+ if action == STANDARD_ACTIONS[:token][:end_point]
358
+ token = parse_response_id(response)
359
+ (!token.nil? && !token.empty?)
360
+ elsif response
361
+ return SUCCESS_STATUS.include? response['data']['status'] if response['data']
362
+ end
363
+ end
364
+
365
+ def message_from(response, action)
366
+ if success_from(response, action)
367
+ if action == STANDARD_ACTIONS[:token][:end_point]
368
+ return response['data']['id']
369
+ else
370
+ return response['data']['status']
371
+ end
372
+ else
373
+ return response['message']
374
+ end
375
+ end
376
+
377
+ def parse_response_id(response)
378
+ response['data']['id'] if response && response['data']
379
+ end
380
+
381
+ def post_data(params)
382
+ params.map { |k, v| "#{k}=#{CGI.escape(v.to_s)}" }.join('&')
383
+ end
384
+
385
+ def error_code_from(response, action)
386
+ response['status_code'] unless success_from(response, action)
387
+ end
388
+ end
389
+ end
390
+ end