Class: SolidusStripe::Gateway

Inherits:
Object
  • Object
show all
Includes:
LogEntries, MoneyToStripeAmountConverter
Defined in:
app/models/solidus_stripe/gateway.rb

Overview

About fractional amounts

All methods in the Gateway class will have amount_in_cents arguments representing the fractional amount as defined by Spree::Money and will be translated to the fractional expected by Stripe, although for most currencies it's cents some will have a different multiplier and that is already took into account.

Constant Summary

Constants included from MoneyToStripeAmountConverter

MoneyToStripeAmountConverter::DIVISIBLE_BY_100, MoneyToStripeAmountConverter::THREE_DECIMAL_CURRENCIES, MoneyToStripeAmountConverter::ZERO_DECIMAL_CURRENCIES

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from LogEntries

#build_payment_log, #payment_log

Methods included from MoneyToStripeAmountConverter

#solidus_decimal_to_subunit, #solidus_subunit_to_decimal, #to_solidus_amount, #to_stripe_amount

Constructor Details

#initialize(options) ⇒ Gateway

Returns a new instance of Gateway.



24
25
26
27
28
29
30
# File 'app/models/solidus_stripe/gateway.rb', line 24

def initialize(options)
  # Cannot use kwargs because of how the Gateway is initialized by Solidus.
  @client = Stripe::StripeClient.new(
    api_key: options.fetch(:api_key, nil),
  )
  @options = options
end

Instance Attribute Details

#clientObject (readonly)

Returns the value of attribute client.



32
33
34
# File 'app/models/solidus_stripe/gateway.rb', line 32

def client
  @client
end

Instance Method Details

#authorize(amount_in_cents, _source, options = {}) ⇒ Object

Authorizes a certain amount on the provided payment source.

We create and confirm the Stripe payment intent in two steps. That's to guarantee that we associate a Solidus payment on the creation, and we can fetch it after the webhook event is published on confirmation.

The Stripe payment intent id is copied over the Solidus payment response_code field.



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'app/models/solidus_stripe/gateway.rb', line 42

def authorize(amount_in_cents, _source, options = {})
  check_given_amount_matches_payment_intent(amount_in_cents, options)

  stripe_payment_intent_options = {
    amount: to_stripe_amount(amount_in_cents, options[:originator].currency),
    confirm: false,
    capture_method: "manual",
  }

  stripe_payment_intent = SolidusStripe::PaymentIntent.prepare_for_payment(
    options[:originator],
    **stripe_payment_intent_options,
  )

  confirm_stripe_payment_intent(stripe_payment_intent.stripe_intent_id)

  build_payment_log(
    success: true,
    message: "PaymentIntent was confirmed successfully",
    response_code: stripe_payment_intent.stripe_intent_id,
    data: stripe_payment_intent.stripe_intent
  )
rescue Stripe::StripeError => e
  build_payment_log(
    success: false,
    message: e.message,
    data: e.response
  )
end

#capture(amount_in_cents, stripe_payment_intent_id, options = {}) ⇒ Object

TODO:

add support for capturing custom amounts

Captures a certain amount from a previously authorized transaction.



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'app/models/solidus_stripe/gateway.rb', line 78

def capture(amount_in_cents, stripe_payment_intent_id, options = {})
  check_given_amount_matches_payment_intent(amount_in_cents, options)
  check_stripe_payment_intent_id(stripe_payment_intent_id)

  stripe_payment_intent = capture_stripe_payment_intent(stripe_payment_intent_id, amount_in_cents)
  build_payment_log(
    success: true,
    message: "PaymentIntent was confirmed successfully",
    response_code: stripe_payment_intent.id,
    data: stripe_payment_intent,
  )
rescue Stripe::InvalidRequestError => e
  build_payment_log(
    success: false,
    message: e.to_s,
    data: e.response,
  )
end

#credit(amount_in_cents, stripe_payment_intent_id, options = {}) ⇒ Object

Refunds the provided amount on a previously captured transaction.

Notice we're adding solidus_skip_sync: 'true' to the metadata to avoid a duplicated refund after the generated webhook event. See RefundsSynchronizer.

TODO: check this method params twice.



161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'app/models/solidus_stripe/gateway.rb', line 161

def credit(amount_in_cents, stripe_payment_intent_id, options = {})
  check_stripe_payment_intent_id(stripe_payment_intent_id)

  payment = options[:originator].payment
  currency = payment.currency

  stripe_refund = request do
    Stripe::Refund.create(
      amount: to_stripe_amount(amount_in_cents, currency),
      payment_intent: stripe_payment_intent_id,
      metadata: RefundsSynchronizer.
    )
  end

  build_payment_log(
    success: true,
    message: "PaymentIntent was refunded successfully",
    response_code: stripe_refund.id,
    data: stripe_refund,
  )
end

#purchase(amount_in_cents, _source, options = {}) ⇒ Object

TODO:

add support for purchasing custom amounts

Authorizes and captures a certain amount on the provided payment source.

See #authorize for how the confirmation step is performed.



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'app/models/solidus_stripe/gateway.rb', line 102

def purchase(amount_in_cents, _source, options = {})
  check_given_amount_matches_payment_intent(amount_in_cents, options)

  stripe_payment_intent_options = {
    amount: to_stripe_amount(amount_in_cents, options[:originator].currency),
    confirm: false,
    capture_method: "automatic",
  }

  stripe_payment_intent = SolidusStripe::PaymentIntent.prepare_for_payment(
    options[:originator],
    **stripe_payment_intent_options,
  )

  confirm_stripe_payment_intent(stripe_payment_intent.stripe_intent_id)

  build_payment_log(
    success: true,
    message: "PaymentIntent was confirmed and captured successfully",
    response_code: stripe_payment_intent.stripe_intent_id,
    data: stripe_payment_intent.stripe_intent,
  )
rescue Stripe::StripeError => e
  build_payment_log(
    success: false,
    message: e.to_s,
    data: e.response,
  )
end

#request { ... } ⇒ Object

Send a request to stripe using the current api keys but ignoring the response object.

Examples:

Retrieve a payment intent

request { Stripe::PaymentIntent.retrieve(intent_id) }

Yields:

  • Allows to use the Stripe gem using the credentials attached to the current payment method

Returns:

  • forwards the result of the block



193
194
195
196
# File 'app/models/solidus_stripe/gateway.rb', line 193

def request(&block)
  result, _response = client.request(&block)
  result
end

#void(stripe_payment_intent_id, _options = {}) ⇒ Object

Voids a previously authorized transaction, releasing the funds that are on hold.



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'app/models/solidus_stripe/gateway.rb', line 133

def void(stripe_payment_intent_id, _options = {})
  check_stripe_payment_intent_id(stripe_payment_intent_id)

  stripe_payment_intent = request do
    Stripe::PaymentIntent.cancel(stripe_payment_intent_id)
  end

  build_payment_log(
    success: true,
    message: "PaymentIntent was canceled successfully",
    response_code: stripe_payment_intent_id,
    data: stripe_payment_intent,
  )
rescue Stripe::InvalidRequestError => e
  build_payment_log(
    success: false,
    message: e.to_s,
    data: e.response,
  )
end