activemerchant 1.63.0 → 1.64.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a16e6e0f311ac3b77069a4f05f5f257b6a69fc60
4
- data.tar.gz: 666b86bb155b6ff98a608bf8c38714a54af46627
3
+ metadata.gz: 1974da9f685774b95695ff91430141a164281f64
4
+ data.tar.gz: 5a547f1ff49dcf3d3ef6089d92636f0c92ac47f3
5
5
  SHA512:
6
- metadata.gz: bd479af93c39120f8a447058524d56de326189f0f3fc26ace5af2681166761fc81a967a66368f9d50e4d7a5a0b64c7a4867c7f61dc2b37e368b0aa19141c87c2
7
- data.tar.gz: b9f8ea0507a585f672f9abce5cf4c2359dbeeb781fe8cdda5e05413aa24fa638fc25a0f83279c4143e72278f8cf4e82f12a1987190390640a7c21ff52f488389
6
+ metadata.gz: 2beea9543a20c61f6549a94d71a6c6d73962cdc0ca21548d378a4ffbf26ade9df8b0cb0d7cb14b7f1355d4999625e9cae82fe3a87572070a18fcbcb81168f83b
7
+ data.tar.gz: 1624eb5f2942edcff808a3ac70fb693c0c7f617c229e384d279f2e8b517558243bd2943a808198b59f378d7f8752cb2807d84717a3b72fa9119caf2e809656a0
data/CHANGELOG CHANGED
@@ -2,6 +2,32 @@
2
2
 
3
3
  == HEAD
4
4
 
5
+ == Version 1.64.0 (March 6, 2017)
6
+ * Authorize.net: Allow settings to be passed for CIM purchases [fwilkins] #2300
7
+ * Authorize.net: Use new `unsupported_feature` standard error code [jasonwebster] #2322
8
+ * Base Gateway: Add new `unsupported_feature` standard error code [jasonwebster] #2322
9
+ * Braintree Blue: Pass cardholder_name with card [curiousepic] #2324
10
+ * Braintree: Add Android Pay meta data fields [jknipp] #2347
11
+ * CardStream: Add additional of currencies [shasum] #2337
12
+ * Credorax: Return failure response reason [shasum] #2341
13
+ * Digitzs: Add gateway [davidsantoso]
14
+ * Digitzs: Remove merchant_id from gateway credentials [davidsantoso]
15
+ * GlobalCollect: Pass options to Refund [curiousepic] #2330
16
+ * Kushki: Add new gateway [shasum] #2326
17
+ * Kushki: Remove body from void call [shasum] #2348
18
+ * Linkpoint: Raise ArgumentError when trying to instantiate without `:pem` [jasonwebster] #2329
19
+ * Omise: Enable Japan, JPY and JCB support [zdk] #2284
20
+ * PayU LATAM: Count pending refunds as succeeded [curiousepic] #2336
21
+ * PayU LATAM: Let Refund take amount value [curiousepic] #2334
22
+ * Paymill: Send new required fields on tokenization requests [tschelabaumann] #2279
23
+ * Revert "Authorize.net: Allow settings to be passed for CIM purchases" [curiousepic] #2339
24
+ * Sage: Default billing state when outside US [shasum] #2340
25
+ * Stripe: Remove idempotency key from verify [shasum] #2335
26
+ * TransFirst Transaction Express: Don't send order_id with refunds [curiousepic] #2350
27
+ * TransFirst Transaction Express: Fix improper AVS and CVV response code mapping [shasum] #2342
28
+ * WePay: Update API version [shasum] #2349
29
+ * USA ePay Advanced: Add quick_update_customer action [joshreeves] #2229
30
+
5
31
  == Version 1.63.0 (February 2, 2017)
6
32
  * Authorize.net: Add #unstore support [jimryan] #2293
7
33
  * AuthorizeNet: Fix line items quirk [shasum]
data/README.md CHANGED
@@ -162,7 +162,7 @@ The [ActiveMerchant Wiki](http://github.com/activemerchant/active_merchant/wikis
162
162
  * [NETPAY Gateway](http://www.netpay.com.mx) - MX
163
163
  * [NMI](http://nmi.com/) - US
164
164
  * [Ogone](http://www.ogone.com/) - BE, DE, FR, NL, AT, CH
165
- * [Omise](https://www.omise.co/) - TH
165
+ * [Omise](https://www.omise.co/) - TH, JP
166
166
  * [Openpay](Openpay) - MX
167
167
  * [Optimal Payments](http://www.optimalpayments.com/) - CA, US, GB
168
168
  * [Orbital Paymentech](http://chasepaymentech.com/) - US, CA
@@ -77,6 +77,9 @@ module ActiveMerchant #:nodoc:
77
77
  # :call_issuer - Transaction requires voice authentication, call issuer
78
78
  # :pickup_card - Issuer requests that you pickup the card from merchant
79
79
  # :test_mode_live_card - Card was declined. Request was in test mode, but used a non test card.
80
+ # :unsupported_feature - Transaction failed due to gateway or merchant
81
+ # configuration not supporting a feature used, such
82
+ # as network tokenization.
80
83
 
81
84
  STANDARD_ERROR_CODE = {
82
85
  :incorrect_number => 'incorrect_number',
@@ -93,7 +96,8 @@ module ActiveMerchant #:nodoc:
93
96
  :call_issuer => 'call_issuer',
94
97
  :pickup_card => 'pick_up_card',
95
98
  :config_error => 'config_error',
96
- :test_mode_live_card => 'test_mode_live_card'
99
+ :test_mode_live_card => 'test_mode_live_card',
100
+ :unsupported_feature => 'unsupported_feature',
97
101
  }
98
102
 
99
103
  cattr_reader :implementations
@@ -36,24 +36,25 @@ module ActiveMerchant
36
36
  }
37
37
 
38
38
  STANDARD_ERROR_CODE_MAPPING = {
39
- '36' => STANDARD_ERROR_CODE[:incorrect_number],
40
- '237' => STANDARD_ERROR_CODE[:invalid_number],
41
- '2315' => STANDARD_ERROR_CODE[:invalid_number],
42
- '37' => STANDARD_ERROR_CODE[:invalid_expiry_date],
43
- '2316' => STANDARD_ERROR_CODE[:invalid_expiry_date],
44
- '378' => STANDARD_ERROR_CODE[:invalid_cvc],
45
- '38' => STANDARD_ERROR_CODE[:expired_card],
46
- '2317' => STANDARD_ERROR_CODE[:expired_card],
47
- '244' => STANDARD_ERROR_CODE[:incorrect_cvc],
48
- '227' => STANDARD_ERROR_CODE[:incorrect_address],
49
39
  '2127' => STANDARD_ERROR_CODE[:incorrect_address],
50
40
  '22' => STANDARD_ERROR_CODE[:card_declined],
41
+ '227' => STANDARD_ERROR_CODE[:incorrect_address],
51
42
  '23' => STANDARD_ERROR_CODE[:card_declined],
52
- '3153' => STANDARD_ERROR_CODE[:processing_error],
43
+ '2315' => STANDARD_ERROR_CODE[:invalid_number],
44
+ '2316' => STANDARD_ERROR_CODE[:invalid_expiry_date],
45
+ '2317' => STANDARD_ERROR_CODE[:expired_card],
53
46
  '235' => STANDARD_ERROR_CODE[:processing_error],
47
+ '237' => STANDARD_ERROR_CODE[:invalid_number],
54
48
  '24' => STANDARD_ERROR_CODE[:pickup_card],
49
+ '244' => STANDARD_ERROR_CODE[:incorrect_cvc],
55
50
  '300' => STANDARD_ERROR_CODE[:config_error],
56
- '384' => STANDARD_ERROR_CODE[:config_error]
51
+ '3153' => STANDARD_ERROR_CODE[:processing_error],
52
+ '3155' => STANDARD_ERROR_CODE[:unsupported_feature],
53
+ '36' => STANDARD_ERROR_CODE[:incorrect_number],
54
+ '37' => STANDARD_ERROR_CODE[:invalid_expiry_date],
55
+ '378' => STANDARD_ERROR_CODE[:invalid_cvc],
56
+ '38' => STANDARD_ERROR_CODE[:expired_card],
57
+ '384' => STANDARD_ERROR_CODE[:config_error],
57
58
  }
58
59
 
59
60
  MARKET_TYPE = {
@@ -573,7 +573,7 @@ module ActiveMerchant #:nodoc:
573
573
  :number => credit_card_or_vault_id.number,
574
574
  :expiration_month => credit_card_or_vault_id.month.to_s.rjust(2, "0"),
575
575
  :expiration_year => credit_card_or_vault_id.year.to_s,
576
- :cardholder_name => "#{credit_card_or_vault_id.first_name} #{credit_card_or_vault_id.last_name}",
576
+ :cardholder_name => credit_card_or_vault_id.name,
577
577
  :cryptogram => credit_card_or_vault_id.payment_cryptogram
578
578
  }
579
579
  elsif credit_card_or_vault_id.source == :android_pay
@@ -582,7 +582,9 @@ module ActiveMerchant #:nodoc:
582
582
  :cryptogram => credit_card_or_vault_id.payment_cryptogram,
583
583
  :expiration_month => credit_card_or_vault_id.month.to_s.rjust(2, "0"),
584
584
  :expiration_year => credit_card_or_vault_id.year.to_s,
585
- :google_transaction_id => credit_card_or_vault_id.transaction_id
585
+ :google_transaction_id => credit_card_or_vault_id.transaction_id,
586
+ :source_card_type => credit_card_or_vault_id.brand,
587
+ :source_card_last_four => credit_card_or_vault_id.last_digits
586
588
  }
587
589
  end
588
590
  else
@@ -590,7 +592,8 @@ module ActiveMerchant #:nodoc:
590
592
  :number => credit_card_or_vault_id.number,
591
593
  :cvv => credit_card_or_vault_id.verification_value,
592
594
  :expiration_month => credit_card_or_vault_id.month.to_s.rjust(2, "0"),
593
- :expiration_year => credit_card_or_vault_id.year.to_s
595
+ :expiration_year => credit_card_or_vault_id.year.to_s,
596
+ :cardholder_name => credit_card_or_vault_id.name
594
597
  }
595
598
  end
596
599
  end
@@ -14,24 +14,95 @@ module ActiveMerchant #:nodoc:
14
14
 
15
15
  CURRENCY_CODES = {
16
16
  "AED" => "784",
17
+ "ALL" => "008",
18
+ "AMD" => "051",
19
+ "ANG" => "532",
20
+ "ARS" => "032",
17
21
  "AUD" => "036",
22
+ "AWG" => "533",
23
+ "BAM" => "977",
24
+ "BBD" => "052",
25
+ "BGN" => "975",
26
+ "BMD" => "060",
27
+ "BOB" => "068",
18
28
  "BRL" => "986",
29
+ "BSD" => "044",
30
+ "BWP" => "072",
31
+ "BZD" => "084",
19
32
  "CAD" => "124",
20
33
  "CHF" => "756",
34
+ "CLP" => "152",
35
+ "CNY" => "156",
36
+ "COP" => "170",
37
+ "CRC" => "188",
21
38
  "CZK" => "203",
22
39
  "DKK" => "208",
40
+ "DOP" => "214",
41
+ "EGP" => "818",
23
42
  "EUR" => "978",
24
43
  "GBP" => "826",
44
+ "GEL" => "981",
45
+ "GIP" => "292",
46
+ "GTQ" => "320",
47
+ "GYD" => "328",
25
48
  "HKD" => "344",
26
- "ICK" => "352",
49
+ "HNL" => "340",
50
+ "HRK" => "191",
51
+ "HUF" => "348",
52
+ "ISK" => "352",
53
+ "IDR" => "360",
54
+ "ILS" => "376",
55
+ "INR" => "356",
27
56
  "JPY" => "392",
57
+ "JMD" => "388",
58
+ "KES" => "404",
59
+ "KRW" => "410",
60
+ "KYD" => "136",
61
+ "LBP" => "422",
62
+ "LKR" => "144",
63
+ "MAD" => "504",
64
+ "MVR" => "462",
65
+ "MWK" => "454",
28
66
  "MXN" => "484",
67
+ "MYR" => "458",
68
+ "NAD" => "516",
69
+ "NGN" => "566",
70
+ "NIO" => "558",
29
71
  "NOK" => "578",
72
+ "NPR" => "524",
30
73
  "NZD" => "554",
74
+ "PAB" => "590",
31
75
  "PEN" => "604",
76
+ "PGK" => "598",
77
+ "PHP" => "608",
78
+ "PKR" => "586",
79
+ "PLN" => "985",
80
+ "PYG" => "600",
81
+ "QAR" => "634",
82
+ "RON" => "946",
83
+ "RSD" => "941",
84
+ "RUB" => "643",
85
+ "RWF" => "646",
86
+ "SAR" => "682",
32
87
  "SEK" => "752",
33
88
  "SGD" => "702",
89
+ "SRD" => "968",
90
+ "THB" => "764",
91
+ "TND" => "788",
92
+ "TRY" => "949",
93
+ "TTD" => "780",
94
+ "TWD" => "901",
95
+ "TZS" => "834",
96
+ "UAH" => "980",
97
+ "UGX" => "800",
34
98
  "USD" => "840",
99
+ "UYU" => "858",
100
+ "VND" => "704",
101
+ "WST" => "882",
102
+ "XAF" => "950",
103
+ "XCD" => "951",
104
+ "XOF" => "952",
105
+ "ZAR" => "710"
35
106
  }
36
107
 
37
108
  CVV_CODE = {
@@ -20,6 +20,83 @@ module ActiveMerchant #:nodoc:
20
20
  self.money_format = :cents
21
21
  self.supported_cardtypes = [:visa, :master, :maestro]
22
22
 
23
+ RESPONSE_MESSAGES = {
24
+ "00" => "Approved or completed successfully",
25
+ "01" => "Refer to card issuer",
26
+ "02" => "Refer to card issuer special condition",
27
+ "03" => "Invalid merchant",
28
+ "04" => "Pick up card",
29
+ "05" => "Do not Honour",
30
+ "06" => "Invalid Transaction for Terminal",
31
+ "07" => "Pick up card special condition",
32
+ "08" => "Time-Out",
33
+ "09" => "No Original",
34
+ "10" => "Approved for partial amount",
35
+ "11" => "Partial Approval",
36
+ "12" => "Invalid transaction card / issuer / acquirer",
37
+ "13" => "Invalid amount",
38
+ "14" => "Invalid card number",
39
+ "17" => "Invalid Capture date (terminal business date)",
40
+ "19" => "System Error; Re-enter transaction",
41
+ "20" => "No From Account",
42
+ "21" => "No To Account",
43
+ "22" => "No Checking Account",
44
+ "23" => "No Saving Account",
45
+ "24" => "No Credit Account",
46
+ "30" => "Format error",
47
+ "34" => "Implausible card data",
48
+ "39" => "Transaction Not Allowed",
49
+ "41" => "Lost Card, Pickup",
50
+ "42" => "Special Pickup",
51
+ "43" => "Hot Card, Pickup (if possible)",
52
+ "44" => "Pickup Card",
53
+ "51" => "Not sufficient funds",
54
+ "52" => "No checking Account",
55
+ "53" => "No savings account",
56
+ "54" => "Expired card",
57
+ "55" => "Pin incorrect",
58
+ "57" => "Transaction not allowed for cardholder",
59
+ "58" => "Transaction not allowed for merchant",
60
+ "59" => "Suspected Fraud",
61
+ "61" => "Exceeds withdrawal amount limit",
62
+ "62" => "Restricted card",
63
+ "63" => "MAC Key Error",
64
+ "65" => "Activity count limit exceeded",
65
+ "66" => "Exceeds Acquirer Limit",
66
+ "67" => "Retain Card; no reason specified",
67
+ "68" => "Response received too late",
68
+ "75" => "Pin tries exceeded",
69
+ "76" => "Invalid Account",
70
+ "77" => "Issuer Does Not Participate In The Service",
71
+ "78" => "Function Not Available",
72
+ "79" => "Key Validation Error",
73
+ "80" => "Approval for Purchase Amount Only",
74
+ "81" => "Unable to Verify PIN",
75
+ "82" => "Time out at issuer system",
76
+ "83" => "Not declined (Valid for all zero amount transactions)",
77
+ "84" => "Invalid Life Cycle of transaction",
78
+ "85" => "Not declined",
79
+ "86" => "Cannot verify pin",
80
+ "87" => "Purchase amount only, no cashback allowed",
81
+ "88" => "MAC sync Error",
82
+ "89" => "Security Violation",
83
+ "91" => "Issuer not available",
84
+ "92" => "Unable to route at acquirer Module",
85
+ "93" => "Transaction cannot be completed",
86
+ "94" => "Duplicate transaction",
87
+ "95" => "Contact Acquirer",
88
+ "96" => "System malfunction",
89
+ "97" => "No Funds Transfer",
90
+ "98" => "Duplicate Reversal",
91
+ "99" => "Duplicate Transaction",
92
+ "N3" => "Cash Service Not Available",
93
+ "N4" => "Cash Back Request Exceeds Issuer Limit",
94
+ "N7" => "N7 (visa), Decline CVV2 failure",
95
+ "R0" => "Stop Payment Order",
96
+ "R1" => "Revocation of Authorisation Order",
97
+ "R3" => "Revocation of all Authorisations Order"
98
+ }
99
+
23
100
  def initialize(options={})
24
101
  requires!(options, :merchant_id, :cipher_key)
25
102
  super
@@ -225,7 +302,7 @@ module ActiveMerchant #:nodoc:
225
302
  if success_from(response)
226
303
  "Succeeded"
227
304
  else
228
- response["Z3"] || "Unable to read error message"
305
+ RESPONSE_MESSAGES[response["Z6"]] || response["Z3"] || "Unable to read error message"
229
306
  end
230
307
  end
231
308
  end
@@ -0,0 +1,292 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class DigitzsGateway < Gateway
4
+ include Empty
5
+
6
+ self.test_url = 'https://beta.digitzsapi.com/sandbox'
7
+ self.live_url = 'https://beta.digitzsapi.com/v3'
8
+
9
+ self.supported_countries = ['US']
10
+ self.default_currency = 'USD'
11
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover]
12
+ self.money_format = :cents
13
+
14
+ self.homepage_url = 'https://digitzs.com'
15
+ self.display_name = 'Digitzs'
16
+
17
+ def initialize(options={})
18
+ requires!(options, :app_key, :api_key)
19
+ super
20
+ end
21
+
22
+ def purchase(money, payment, options={})
23
+ MultiResponse.run do |r|
24
+ r.process { commit("auth/token", app_token_request(options)) }
25
+ r.process { commit('payments', purchase_request(money, payment, options), options.merge({ app_token: app_token_from(r) })) }
26
+ end
27
+ end
28
+
29
+ def refund(money, authorization, options={})
30
+ MultiResponse.run do |r|
31
+ r.process { commit("auth/token", app_token_request(options)) }
32
+ r.process { commit('payments', refund_request(money, authorization, options), options.merge({ app_token: app_token_from(r) })) }
33
+ end
34
+ end
35
+
36
+ def store(payment, options = {})
37
+ MultiResponse.run do |r|
38
+ r.process { commit("auth/token", app_token_request(options)) }
39
+ options.merge!({ app_token: app_token_from(r) })
40
+
41
+ if options[:customer_id].present?
42
+ customer_id = check_customer_exists(options)
43
+
44
+ if customer_id
45
+ r.process { add_credit_card_to_customer(payment, options) }
46
+ else
47
+ r.process { add_customer_with_credit_card(payment, options) }
48
+ end
49
+ else
50
+ r.process { add_customer_with_credit_card(payment, options) }
51
+ end
52
+ end
53
+ end
54
+
55
+ def supports_scrubbing?
56
+ true
57
+ end
58
+
59
+ def scrub(transcript)
60
+ transcript.
61
+ gsub(%r((Authorization: Bearer ).+), '\1[FILTERED]').
62
+ gsub(%r((X-Api-Key: )\w+), '\1[FILTERED]').
63
+ gsub(%r((\"id\\\":\\\").+), '\1[FILTERED]').
64
+ gsub(%r((\"appKey\\\":\\\").+), '\1[FILTERED]').
65
+ gsub(%r((\"appToken\\\":\\\").+), '\1[FILTERED]').
66
+ gsub(%r((\"code\\\":\\\")\d+), '\1[FILTERED]').
67
+ gsub(%r((\"number\\\":\\\")\d+), '\1[FILTERED]')
68
+ end
69
+
70
+ private
71
+
72
+ def new_post
73
+ {
74
+ data: {
75
+ attributes: {}
76
+ }
77
+ }
78
+ end
79
+
80
+ def add_split(post, options)
81
+ return unless options[:payment_type] == "card_split" || options[:payment_type] == "token_split"
82
+
83
+ post[:data][:attributes][:split] = {
84
+ merchantId: options[:split_merchant_id],
85
+ amount: amount(options[:split_amount])
86
+ }
87
+ end
88
+
89
+ def add_payment(post, payment, options)
90
+ if payment.is_a? String
91
+ customer_id, token = split_authorization(payment)
92
+ post[:data][:attributes][:token] = {
93
+ customerId: customer_id,
94
+ tokenId: token
95
+ }
96
+ else
97
+ post[:data][:attributes][:card] = {
98
+ type: payment.brand,
99
+ holder: payment.name,
100
+ number: payment.number,
101
+ expiry: expdate(payment),
102
+ code: payment.verification_value
103
+ }
104
+ end
105
+ end
106
+
107
+ def add_transaction(post, money, options)
108
+ post[:data][:attributes][:transaction] = {
109
+ amount: amount(money),
110
+ currency: (options[:currency] || currency(money)),
111
+ invoice: options[:order_id] || generate_unique_id
112
+ }
113
+ end
114
+
115
+ def add_address(post, options)
116
+ if address = options[:billing_address] || options[:address]
117
+ post[:data][:attributes][:billingAddress] = {
118
+ line1: address[:address1] || "",
119
+ line2: address[:address2] || "",
120
+ city: address[:city] || "",
121
+ state: address[:state] || "",
122
+ zip: address[:zip] || "",
123
+ country: address["country"] || "USA"
124
+ }
125
+ end
126
+ end
127
+
128
+ def app_token_request(options)
129
+ post = new_post
130
+ post[:data][:type] = "auth"
131
+ post[:data][:attributes] = { appKey: @options[:app_key] }
132
+
133
+ post
134
+ end
135
+
136
+ def purchase_request(money, payment, options)
137
+ post = new_post
138
+ post[:data][:type] = "payments"
139
+ post[:data][:attributes][:merchantId] = options[:merchant_id]
140
+ post[:data][:attributes][:paymentType] = determine_payment_type(payment, options)
141
+ add_split(post, options)
142
+ add_payment(post, payment, options)
143
+ add_transaction(post, money, options)
144
+ add_address(post, options)
145
+
146
+ post
147
+ end
148
+
149
+ def refund_request(money, authorization, options)
150
+ post = new_post
151
+ post[:data][:type] = "payments"
152
+ post[:data][:attributes][:merchantId] = options[:merchant_id]
153
+ post[:data][:attributes][:paymentType] = "cardRefund"
154
+ post[:data][:attributes][:originalTransaction] = {id: authorization}
155
+ add_transaction(post, money, options)
156
+
157
+ post
158
+ end
159
+
160
+ def create_customer_request(payment, options)
161
+ post = new_post
162
+ post[:data][:type] = "customers"
163
+ post[:data][:attributes] = {
164
+ merchantId: options[:merchant_id],
165
+ name: payment.name,
166
+ externalId: "#{SecureRandom.hex(16)}"
167
+ }
168
+
169
+ post
170
+ end
171
+
172
+ def create_token_request(payment, options)
173
+ post = new_post
174
+ post[:data][:type] = "tokens"
175
+ post[:data][:attributes] = {
176
+ tokenType: "card",
177
+ customerId: options[:customer_id],
178
+ label: "Credit Card",
179
+ }
180
+ add_payment(post, payment, options)
181
+ add_address(post, options)
182
+
183
+ post
184
+ end
185
+
186
+ def check_customer_exists(options = {})
187
+ url = (test? ? test_url : live_url)
188
+ response = parse(ssl_get(url + "/customers/#{options[:customer_id]}", headers(options)))
189
+
190
+ return response.try(:[], "data").try(:[], "customerId") if success_from(response)
191
+ return nil
192
+ end
193
+
194
+ def add_credit_card_to_customer(payment, options = {})
195
+ commit('tokens', create_token_request(payment, options), options)
196
+ end
197
+
198
+ def add_customer_with_credit_card(payment, options = {})
199
+ customer_response = commit('customers', create_customer_request(payment, options), options)
200
+ options.merge!({customer_id: customer_response.authorization})
201
+ commit('tokens', create_token_request(payment, options), options)
202
+ end
203
+
204
+ def parse(body)
205
+ JSON.parse(body)
206
+ end
207
+
208
+ def commit(action, parameters, options={})
209
+ url = (test? ? test_url : live_url)
210
+ response = parse(ssl_post(url + "/#{action}", parameters.to_json, headers(options)))
211
+
212
+ Response.new(
213
+ success_from(response),
214
+ message_from(response),
215
+ response,
216
+ authorization: authorization_from(response),
217
+ avs_result: AVSResult.new(code: avs_result_from(response)),
218
+ cvv_result: CVVResult.new(cvv_result_from(response)),
219
+ test: test?,
220
+ error_code: error_code_from(response)
221
+ )
222
+ end
223
+
224
+ def success_from(response)
225
+ response["errors"].nil? && response["message"].nil?
226
+ end
227
+
228
+ def message_from(response)
229
+ return response["message"] if response["message"]
230
+ return "Success" if success_from(response)
231
+ response["errors"].map {|error_hash| error_hash["detail"] }.join(", ")
232
+ end
233
+
234
+ def authorization_from(response)
235
+ if customer_id = response.try(:[], "data").try(:[], "attributes").try(:[], "customerId")
236
+ "#{customer_id}|#{response.try(:[], "data").try(:[], "id")}"
237
+ else
238
+ response.try(:[], "data").try(:[], "id")
239
+ end
240
+ end
241
+
242
+ def avs_result_from(response)
243
+ response.try(:[], "data").try(:[], "attributes").try(:[], "transaction").try(:[], "avsResult")
244
+ end
245
+
246
+ def cvv_result_from(response)
247
+ response.try(:[], "data").try(:[], "attributes").try(:[], "transaction").try(:[], "codeResult")
248
+ end
249
+
250
+ def app_token_from(response)
251
+ response.params.try(:[], "data").try(:[], "attributes").try(:[], "appToken")
252
+ end
253
+
254
+ def headers(options)
255
+ headers = {
256
+ 'Content-Type' => 'application/json',
257
+ 'x-api-key' => @options[:api_key]
258
+ }
259
+
260
+ headers.merge!({"Authorization" => "Bearer #{options[:app_token]}"}) if options[:app_token]
261
+ headers
262
+ end
263
+
264
+ def error_code_from(response)
265
+ unless success_from(response)
266
+ response["errors"].nil? ? response["message"] : response["errors"].map {|error_hash| error_hash["code"] }.join(", ")
267
+ end
268
+ end
269
+
270
+ def split_authorization(authorization)
271
+ customer_id, token = authorization.split("|")
272
+ [customer_id, token]
273
+ end
274
+
275
+ def determine_payment_type(payment, options)
276
+ return "cardSplit" if options[:payment_type] == "card_split"
277
+ return "tokenSplit" if options[:payment_type] == "token_split"
278
+ return "token" if payment.is_a? String
279
+ "card"
280
+ end
281
+
282
+ def handle_response(response)
283
+ case response.code.to_i
284
+ when 200..499
285
+ response.body
286
+ else
287
+ raise ResponseError.new(response)
288
+ end
289
+ end
290
+ end
291
+ end
292
+ end