Class: ActiveMerchant::Billing::StripePaymentIntentsGateway

Inherits:
StripeGateway show all
Defined in:
lib/active_merchant/billing/gateways/stripe_payment_intents.rb

Overview

This gateway uses the current Stripe Payment Intents API. For the legacy API, see the Stripe gateway

Constant Summary collapse

ALLOWED_METHOD_STATES =
%w[automatic manual].freeze
ALLOWED_CANCELLATION_REASONS =
%w[duplicate fraudulent requested_by_customer abandoned].freeze
CREATE_INTENT_ATTRIBUTES =
%i[description statement_descriptor_suffix statement_descriptor receipt_email save_payment_method]
CONFIRM_INTENT_ATTRIBUTES =
%i[receipt_email return_url save_payment_method setup_future_usage off_session]
UPDATE_INTENT_ATTRIBUTES =
%i[description statement_descriptor_suffix statement_descriptor receipt_email setup_future_usage]
DEFAULT_API_VERSION =
'2020-08-27'

Constants inherited from StripeGateway

ActiveMerchant::Billing::StripeGateway::AVS_CODE_TRANSLATOR, ActiveMerchant::Billing::StripeGateway::BANK_ACCOUNT_HOLDER_TYPE_MAPPING, ActiveMerchant::Billing::StripeGateway::CVC_CODE_TRANSLATOR, ActiveMerchant::Billing::StripeGateway::MINIMUM_AUTHORIZE_AMOUNTS, ActiveMerchant::Billing::StripeGateway::STANDARD_ERROR_CODE_MAPPING

Constants inherited from Gateway

Gateway::CREDIT_DEPRECATION_MESSAGE, Gateway::RECURRING_DEPRECATION_MESSAGE, Gateway::STANDARD_ERROR_CODE

Instance Attribute Summary

Attributes inherited from Gateway

#options

Instance Method Summary collapse

Methods inherited from StripeGateway

#delete_latest_test_external_account, #initialize, #refund_application_fee, #scrub, #supports_network_tokenization?, #supports_scrubbing?, #tokenize_apple_pay_token, #update, #update_customer, #verify_credentials

Methods inherited from Gateway

#add_field_to_post_if_present, #add_fields_to_post_if_present, card_brand, #card_brand, #generate_unique_id, inherited, #initialize, #scrub, supported_countries, #supported_countries, supported_countries=, supports?, #supports_network_tokenization?, #supports_scrubbing?, #test?

Methods included from CreditCardFormatting

#expdate, #format

Methods included from PostsData

included, #raw_ssl_request, #ssl_get, #ssl_post, #ssl_request

Constructor Details

This class inherits a constructor from ActiveMerchant::Billing::StripeGateway

Instance Method Details

#add_payment_method_card_data_token(post_data, payment_method) ⇒ Object

[View source]

97
98
99
100
101
102
# File 'lib/active_merchant/billing/gateways/stripe_payment_intents.rb', line 97

def add_payment_method_card_data_token(post_data, payment_method)
  post_data.merge!({
    payment_method_types: ['card'],
    payment_method_data: { type: 'card', card: { token: payment_method } }
  })
end

#add_payment_method_data(payment_method, options = {}) ⇒ Object

[View source]

84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/active_merchant/billing/gateways/stripe_payment_intents.rb', line 84

def add_payment_method_data(payment_method, options = {})
  post_data = {}
  post_data[:type] = 'card'
  post_data[:card] = {}
  post_data[:card][:number] = payment_method.number
  post_data[:card][:exp_month] = payment_method.month
  post_data[:card][:exp_year] = payment_method.year
  post_data[:card][:cvc] = payment_method.verification_value if payment_method.verification_value
  add_billing_address(post_data, options)
  add_name_only(post_data, payment_method) if post_data[:billing_details].nil?
  post_data
end

#authorize(money, payment_method, options = {}) ⇒ Object

[View source]

156
157
158
# File 'lib/active_merchant/billing/gateways/stripe_payment_intents.rb', line 156

def authorize(money, payment_method, options = {})
  create_intent(money, payment_method, options.merge!(confirm: true, capture_method: 'manual'))
end

#capture(money, intent_id, options = {}) ⇒ Object

[View source]

164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/active_merchant/billing/gateways/stripe_payment_intents.rb', line 164

def capture(money, intent_id, options = {})
  post = {}
  currency = options[:currency] || currency(money)
  post[:amount_to_capture] = localized_amount(money, currency)
  if options[:transfer_amount]
    post[:transfer_data] = {}
    post[:transfer_data][:amount] = options[:transfer_amount]
  end
  post[:application_fee_amount] = options[:application_fee] if options[:application_fee]
  options = format_idempotency_key(options, 'capture')
  commit(:post, "payment_intents/#{intent_id}/capture", post, options)
end

#confirm_intent(intent_id, payment_method, options = {}) ⇒ Object

[View source]

64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/active_merchant/billing/gateways/stripe_payment_intents.rb', line 64

def confirm_intent(intent_id, payment_method, options = {})
  post = {}
  result = add_payment_method_token(post, payment_method, options)
  return result if result.is_a?(ActiveMerchant::Billing::Response)

  add_payment_method_types(post, options)
  CONFIRM_INTENT_ATTRIBUTES.each do |attribute|
    add_whitelisted_attribute(post, options, attribute)
  end

  commit(:post, "payment_intents/#{intent_id}/confirm", post, options)
end

#create_intent(money, payment_method, options = {}) ⇒ Object

[View source]

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/active_merchant/billing/gateways/stripe_payment_intents.rb', line 15

def create_intent(money, payment_method, options = {})
  MultiResponse.run do |r|
    if payment_method.is_a?(NetworkTokenizationCreditCard)
      r.process { tokenize_apple_google(payment_method, options) }
      payment_method = (r.params['token']['id']) if r.success?
    end
    r.process do
      post = {}
      add_amount(post, money, options, true)
      add_capture_method(post, options)
      add_confirmation_method(post, options)
      add_customer(post, options)

      result = add_payment_method_token(post, payment_method, options)
      return result if result.is_a?(ActiveMerchant::Billing::Response)

      add_external_three_d_secure_auth_data(post, options)
      (post, options)
      add_return_url(post, options)
      (post, options)
      add_radar_data(post, options)
      add_shipping_address(post, options)
      setup_future_usage(post, options)
      add_exemption(post, options)
      add_stored_credentials(post, options)
      add_ntid(post, options)
      add_claim_without_transaction_id(post, options)
      add_error_on_requires_action(post, options)
      add_fulfillment_date(post, options)
      request_three_d_secure(post, options)
      add_level_three(post, options)

      CREATE_INTENT_ATTRIBUTES.each do |attribute|
        add_whitelisted_attribute(post, options, attribute)
      end
      commit(:post, 'payment_intents', post, options)
    end
  end
end

#create_payment_method(payment_method, options = {}) ⇒ Object

[View source]

77
78
79
80
81
82
# File 'lib/active_merchant/billing/gateways/stripe_payment_intents.rb', line 77

def create_payment_method(payment_method, options = {})
  post_data = add_payment_method_data(payment_method, options)

  options = format_idempotency_key(options, 'pm')
  commit(:post, 'payment_methods', post_data, options)
end

#create_setup_intent(payment_method, options = {}) ⇒ Object

[View source]

124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/active_merchant/billing/gateways/stripe_payment_intents.rb', line 124

def create_setup_intent(payment_method, options = {})
  MultiResponse.run do |r|
    r.process do
      post = {}
      add_customer(post, options)
      result = add_payment_method_token(post, payment_method, options, r)
      return result if result.is_a?(ActiveMerchant::Billing::Response)

      (post, options)
      add_return_url(post, options)
      add_fulfillment_date(post, options)
      request_three_d_secure(post, options)
      post[:on_behalf_of] = options[:on_behalf_of] if options[:on_behalf_of]
      post[:usage] = options[:usage] if %w(on_session off_session).include?(options[:usage])
      post[:description] = options[:description] if options[:description]

      commit(:post, 'setup_intents', post, options)
    end
  end
end

#create_test_customerObject

[View source]

59
60
61
62
# File 'lib/active_merchant/billing/gateways/stripe_payment_intents.rb', line 59

def create_test_customer
  response = api_request(:post, 'customers')
  response['id']
end

#purchase(money, payment_method, options = {}) ⇒ Object

[View source]

160
161
162
# File 'lib/active_merchant/billing/gateways/stripe_payment_intents.rb', line 160

def purchase(money, payment_method, options = {})
  create_intent(money, payment_method, options.merge!(confirm: true, capture_method: 'automatic'))
end

#refund(money, intent_id, options = {}) ⇒ Object

[View source]

183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/active_merchant/billing/gateways/stripe_payment_intents.rb', line 183

def refund(money, intent_id, options = {})
  if intent_id.include?('pi_')
    intent = api_request(:get, "payment_intents/#{intent_id}", nil, options)

    return Response.new(false, intent['error']['message'], intent) if intent['error']

    charge_id = intent.try(:[], 'charges').try(:[], 'data').try(:[], 0).try(:[], 'id')

    if charge_id.nil?
      error_message = "No associated charge for #{intent['id']}"
      error_message << "; payment_intent has a status of #{intent['status']}" if intent.try(:[], 'status') && intent.try(:[], 'status') != 'succeeded'
      return Response.new(false, error_message, intent)
    end
  else
    charge_id = intent_id
  end

  super(money, charge_id, options)
end

#retrieve_setup_intent(setup_intent_id, options = {}) ⇒ Object

[View source]

145
146
147
148
149
150
151
152
153
154
# File 'lib/active_merchant/billing/gateways/stripe_payment_intents.rb', line 145

def retrieve_setup_intent(setup_intent_id, options = {})
  # Retrieving a setup_intent passing 'expand[]=latest_attempt' allows the caller to
  # check for a network_transaction_id and ds_transaction_id
  # eg (latest_attempt -> payment_method_details -> card -> network_transaction_id)
  #
  # Being able to retrieve these fields enables payment flows that rely on MIT exemptions, e.g: off_session
  commit(:post, "setup_intents/#{setup_intent_id}", {
    'expand[]': 'latest_attempt'
  }, options)
end

#setup_purchase(money, options = {}) ⇒ Object

[View source]

247
248
249
250
251
252
253
254
255
# File 'lib/active_merchant/billing/gateways/stripe_payment_intents.rb', line 247

def setup_purchase(money, options = {})
  requires!(options, :payment_method_types)
  post = {}
  add_currency(post, options, money)
  add_amount(post, money, options)
  add_payment_method_types(post, options)
  (post, options)
  commit(:post, 'payment_intents', post, options)
end

#show_intent(intent_id, options) ⇒ Object

[View source]

55
56
57
# File 'lib/active_merchant/billing/gateways/stripe_payment_intents.rb', line 55

def show_intent(intent_id, options)
  commit(:get, "payment_intents/#{intent_id}", nil, options)
end

#store(payment_method, options = {}) ⇒ Object

Note: Not all payment methods are currently supported by the Payment Methods API Current implementation will create a PaymentMethod object if the method is a token or credit card All other types will default to legacy Stripe store

[View source]

206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/active_merchant/billing/gateways/stripe_payment_intents.rb', line 206

def store(payment_method, options = {})
  params = {}
  post = {}
  # If customer option is provided, create a payment method and attach to customer id
  # Otherwise, create a customer, then attach
  if payment_method.is_a?(StripePaymentToken) || payment_method.is_a?(ActiveMerchant::Billing::CreditCard)
    result = add_payment_method_token(params, payment_method, options)
    return result if result.is_a?(ActiveMerchant::Billing::Response)

    if options[:customer]
      customer_id = options[:customer]
    else
      post[:description] = options[:description] if options[:description]
      post[:email] = options[:email] if options[:email]
      options = format_idempotency_key(options, 'customer')
      post[:expand] = [:sources]
      customer = commit(:post, 'customers', post, options)
      customer_id = customer.params['id']
    end
    options = format_idempotency_key(options, 'attach')
    attach_parameters = { customer: customer_id }
    attach_parameters[:validate] = options[:validate] unless options[:validate].nil?
    commit(:post, "payment_methods/#{params[:payment_method]}/attach", attach_parameters, options)
  else
    super(payment_method, options)
  end
end

#unstore(identification, options = {}, deprecated_options = {}) ⇒ Object

[View source]

234
235
236
237
238
239
240
241
# File 'lib/active_merchant/billing/gateways/stripe_payment_intents.rb', line 234

def unstore(identification, options = {}, deprecated_options = {})
  if identification.include?('pm_')
    _, payment_method = identification.split('|')
    commit(:post, "payment_methods/#{payment_method}/detach", nil, options)
  else
    super(identification, options, deprecated_options)
  end
end

#update_intent(money, intent_id, payment_method, options = {}) ⇒ Object

[View source]

104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/active_merchant/billing/gateways/stripe_payment_intents.rb', line 104

def update_intent(money, intent_id, payment_method, options = {})
  post = {}
  add_amount(post, money, options)

  result = add_payment_method_token(post, payment_method, options)
  return result if result.is_a?(ActiveMerchant::Billing::Response)

  add_payment_method_types(post, options)
  add_customer(post, options)
  (post, options)
  add_shipping_address(post, options)
  (post, options)
  add_fulfillment_date(post, options)

  UPDATE_INTENT_ATTRIBUTES.each do |attribute|
    add_whitelisted_attribute(post, options, attribute)
  end
  commit(:post, "payment_intents/#{intent_id}", post, options)
end

#verify(payment_method, options = {}) ⇒ Object

[View source]

243
244
245
# File 'lib/active_merchant/billing/gateways/stripe_payment_intents.rb', line 243

def verify(payment_method, options = {})
  create_setup_intent(payment_method, options.merge!({ confirm: true, verify: true }))
end

#void(intent_id, options = {}) ⇒ Object

[View source]

177
178
179
180
181
# File 'lib/active_merchant/billing/gateways/stripe_payment_intents.rb', line 177

def void(intent_id, options = {})
  post = {}
  post[:cancellation_reason] = options[:cancellation_reason] if ALLOWED_CANCELLATION_REASONS.include?(options[:cancellation_reason])
  commit(:post, "payment_intents/#{intent_id}/cancel", post, options)
end