spree_stripe 6.0.0.rc1 → 6.0.0.rc2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b65629d2703ce9659457a090c9baf88ef3f7685550a64e6a8c8e3c296531ad15
4
- data.tar.gz: a82e689658e39723dd178006b7937793f128fbff6436a48f1e4ddb2e4f74eed1
3
+ metadata.gz: 8983341a30de22cd7fa7ad6b6d71aebe323935bfdda5ec4ae7644cd0a268d2e9
4
+ data.tar.gz: b249b9acba1b25c1590c960336ee58130ff94a67eb9daf4b300fd508b354a1d5
5
5
  SHA512:
6
- metadata.gz: a5bb7beea57c85809530e0601431e0292943b18636cbaeecffeba60b73cd1493ec13b9b825af477462eed2022b5ae85410461c5259b7258768121c38d451f6b3
7
- data.tar.gz: 5972312842ff35f2ca34fdfdded24cd3f95ea89146951536ae9b53b8111013f6bfba70480cc399cd18b4940153f64651046aa998eac0c61b27189165c19016a4
6
+ metadata.gz: b334eb2bcb2d3be7b32688b51b5585616b895c22c6c012b8da7f42dad3595b65e35f506b58b30bccffc324fc5b704ceb80cb59199ef42470092534ab9b27ba8f
7
+ data.tar.gz: 8e4737ce51b74cbd937790c27a8bc2befab609b1d7391fa9c1e792dfc9a0d11be5b48f9ccb5f63dabb0c5f56acec41ba084b6db23a264e072702cdc65a8c205b
@@ -48,9 +48,9 @@ module SpreeStripe
48
48
  # paid, or an earlier one could not be canceled
49
49
  def create_payment_session(order:, amount: nil, external_data: {})
50
50
  total = amount.presence || order.total_minus_store_credits
51
- amount_in_cents = Spree::Money.new(total, currency: order.currency).cents
51
+ stripe_amount = SpreeStripe::Units.to_stripe(total, order.currency)
52
52
 
53
- raise Spree::Core::GatewayError, I18n.t('spree.stripe.payment_session_errors.zero_amount') if amount_in_cents.zero?
53
+ raise Spree::Core::GatewayError, I18n.t('spree.stripe.payment_session_errors.zero_amount') if stripe_amount.zero?
54
54
 
55
55
  stripe_payment_method_id = external_data[:stripe_payment_method_id] || external_data['stripe_payment_method_id']
56
56
 
@@ -59,7 +59,7 @@ module SpreeStripe
59
59
  gateway_customer = fetch_or_create_customer(order: order)
60
60
 
61
61
  response = create_payment_intent(
62
- amount_in_cents, order,
62
+ stripe_amount, order,
63
63
  payment_method_id: stripe_payment_method_id,
64
64
  customer_profile_id: gateway_customer&.profile_id
65
65
  )
@@ -85,18 +85,18 @@ module SpreeStripe
85
85
 
86
86
  def update_payment_session(payment_session:, amount: nil, external_data: {})
87
87
  attrs = {}
88
- amount_in_cents = nil
88
+ stripe_amount = nil
89
89
 
90
90
  if amount.present?
91
91
  attrs[:amount] = amount
92
- amount_in_cents = Spree::Money.new(amount, currency: payment_session.currency).cents
92
+ stripe_amount = SpreeStripe::Units.to_stripe(amount, payment_session.currency)
93
93
  end
94
94
 
95
95
  stripe_payment_method_id = external_data[:stripe_payment_method_id] || external_data['stripe_payment_method_id']
96
96
 
97
97
  update_payment_intent(
98
98
  payment_session.external_id,
99
- amount_in_cents || payment_session.amount_in_cents,
99
+ stripe_amount || SpreeStripe::Units.to_stripe(payment_session.amount, payment_session.currency),
100
100
  payment_session.owner,
101
101
  stripe_payment_method_id
102
102
  )
@@ -165,8 +165,8 @@ module SpreeStripe
165
165
  send_request { |opts| Stripe::PaymentIntent.confirm(payment_intent_id, {}, opts) }
166
166
  end
167
167
 
168
- def capture_payment_intent(payment_intent_id, amount_in_cents)
169
- send_request { |opts| Stripe::PaymentIntent.capture(payment_intent_id, { amount_to_capture: amount_in_cents }, opts) }
168
+ def capture_payment_intent(payment_intent_id, stripe_amount)
169
+ send_request { |opts| Stripe::PaymentIntent.capture(payment_intent_id, { amount_to_capture: stripe_amount }, opts) }
170
170
  end
171
171
 
172
172
  def cancel_payment_intent(payment_intent_id)
@@ -273,9 +273,9 @@ module SpreeStripe
273
273
 
274
274
  # @param order [Spree::Cart, Spree::Order]
275
275
  # @return [Spree::PaymentResponse]
276
- def create_payment_intent(amount_in_cents, order, payment_method_id: nil, customer_profile_id: nil)
276
+ def create_payment_intent(stripe_amount, order, payment_method_id: nil, customer_profile_id: nil)
277
277
  payload = {
278
- amount: amount_in_cents,
278
+ amount: stripe_amount,
279
279
  currency: order.currency,
280
280
  customer: customer_profile_id,
281
281
  payment_method: payment_method_id,
@@ -304,10 +304,10 @@ module SpreeStripe
304
304
  end
305
305
 
306
306
  # Only the fields that can legitimately change while a session is pending.
307
- def update_payment_intent(payment_intent_id, amount_in_cents, order, payment_method_id = nil)
307
+ def update_payment_intent(payment_intent_id, stripe_amount, order, payment_method_id = nil)
308
308
  protect_from_error do
309
309
  payload = {
310
- amount: amount_in_cents,
310
+ amount: stripe_amount,
311
311
  currency: order.currency,
312
312
  customer: fetch_or_create_customer(order: order)&.profile_id,
313
313
  payment_method: payment_method_id,
@@ -3,6 +3,8 @@ module SpreeStripe
3
3
  # created and confirmed through sessions; core's payment lifecycle then drives
4
4
  # capture, refund and cancellation against the resulting intent.
5
5
  class Gateway < ::Spree::Gateway
6
+ self.accepts_money_amounts = true
7
+
6
8
  include SpreeStripe::Gateway::PaymentSessions
7
9
  include SpreeStripe::Gateway::PaymentSetupSessions
8
10
  include SpreeStripe::Gateway::Webhooks
@@ -86,26 +88,26 @@ module SpreeStripe
86
88
  # is already confirmed client-side in the session flow, so this reads back the
87
89
  # intent rather than initiating a charge.
88
90
  #
89
- # @param amount_in_cents [Integer]
91
+ # @param money [Spree::Money]
90
92
  # @param payment_source [Spree::PaymentSource, Spree::CreditCard]
91
93
  # @param gateway_options [Hash] Spree::Payment::GatewayOptions#to_hash
92
94
  # @return [Spree::PaymentResponse]
93
- def authorize(amount_in_cents, payment_source, gateway_options = {})
94
- handle_authorize_or_purchase(amount_in_cents, payment_source, gateway_options)
95
+ def authorize(money, payment_source, gateway_options = {})
96
+ handle_authorize_or_purchase(money, payment_source, gateway_options)
95
97
  end
96
98
 
97
99
  # @see #authorize — capture vs authorize is decided by the intent's capture
98
100
  # method, so both resolve the same way.
99
- def purchase(amount_in_cents, payment_source, gateway_options = {})
100
- handle_authorize_or_purchase(amount_in_cents, payment_source, gateway_options)
101
+ def purchase(money, payment_source, gateway_options = {})
102
+ handle_authorize_or_purchase(money, payment_source, gateway_options)
101
103
  end
102
104
 
103
- def capture(amount_in_cents, payment_intent_id, _gateway_options = {})
105
+ def capture(money, payment_intent_id, _gateway_options = {})
104
106
  protect_from_error do
105
107
  stripe_payment_intent = retrieve_payment_intent(payment_intent_id)
106
108
 
107
109
  response = if payment_intent_requires_capture?(stripe_payment_intent)
108
- capture_payment_intent(payment_intent_id, amount_in_cents)
110
+ capture_payment_intent(payment_intent_id, SpreeStripe::Units.to_stripe(money, money.currency))
109
111
  elsif stripe_payment_intent.status == 'succeeded'
110
112
  stripe_payment_intent
111
113
  else
@@ -116,10 +118,10 @@ module SpreeStripe
116
118
  end
117
119
  end
118
120
 
119
- def credit(amount_in_cents, _source, payment_intent_id, _gateway_options = {})
121
+ def credit(money, _source, payment_intent_id, _gateway_options = {})
120
122
  protect_from_error do
121
123
  response = send_request do |opts|
122
- Stripe::Refund.create({ amount: amount_in_cents, payment_intent: payment_intent_id }, opts)
124
+ Stripe::Refund.create({ amount: SpreeStripe::Units.to_stripe(money, money.currency), payment_intent: payment_intent_id }, opts)
123
125
  end
124
126
 
125
127
  success(response.id, response)
@@ -269,7 +271,7 @@ module SpreeStripe
269
271
 
270
272
  private
271
273
 
272
- def handle_authorize_or_purchase(amount_in_cents, _payment_source, gateway_options)
274
+ def handle_authorize_or_purchase(_money, _payment_source, gateway_options)
273
275
  # Scoped through this gateway's own payments — a cart-owned payment
274
276
  # (checkout is still in flight) has no order to join through, and the
275
277
  # payment method already belongs to exactly one store. Found by the
@@ -258,7 +258,7 @@ module SpreeStripe
258
258
  wanted = currency.to_s.downcase
259
259
  minor = balance.available.select { |entry| entry.currency == wanted }.sum(&:amount)
260
260
 
261
- Spree::Money::Rounding.from_minor_units(minor, currency)
261
+ SpreeStripe::Units.from_stripe(minor, currency)
262
262
  rescue *AMBIGUOUS_ERRORS => e
263
263
  # The sweep must not read silence as "nothing to send" and settle a
264
264
  # smaller batch than the seller is owed, nor as "no limit" and ask for
@@ -290,8 +290,7 @@ module SpreeStripe
290
290
  return {} if balance_transaction.blank? || balance_transaction.try(:currency).blank?
291
291
 
292
292
  {
293
- settled_amount: Spree::Money::Rounding.from_minor_units(balance_transaction.amount,
294
- balance_transaction.currency),
293
+ settled_amount: SpreeStripe::Units.from_stripe(balance_transaction.amount, balance_transaction.currency),
295
294
  settled_currency: balance_transaction.currency.upcase
296
295
  }
297
296
  end
@@ -322,7 +321,7 @@ module SpreeStripe
322
321
  end
323
322
 
324
323
  def minor_units(record)
325
- Spree::Money::Rounding.to_minor_units(record.amount.abs, record.currency)
324
+ SpreeStripe::Units.to_stripe(record.amount.abs, record.currency, payout: true)
326
325
  end
327
326
 
328
327
  # Ties transfers paid from the platform balance to the checkout they
@@ -0,0 +1,52 @@
1
+ module SpreeStripe
2
+ # Stripe's integer amounts. Mostly the currency's ISO minor unit, with
3
+ # Stripe's own exceptions (https://docs.stripe.com/currencies): its
4
+ # zero-decimal currencies are sent in whole units, and ISK and UGX, zero-decimal
5
+ # since, are still sent as two-decimal values ending in 00. Stripe also takes
6
+ # three-decimal currencies only in steps of 0.010, and pays out HUF and TWD
7
+ # only in whole units; an amount it would refuse raises here, naming the rule,
8
+ # rather than being rounded to a sum other than the one Spree records.
9
+ module Units
10
+ module_function
11
+
12
+ ZERO_DECIMAL = %w[BIF CLP DJF GNF JPY KMF KRW MGA PYG RWF VND VUV XAF XOF XPF].freeze
13
+ WHOLE_IN_HUNDREDTHS = %w[ISK UGX].freeze
14
+ THREE_DECIMAL = %w[BHD JOD KWD OMR TND].freeze
15
+ WHOLE_PAYOUTS = %w[HUF TWD].freeze
16
+
17
+ class UnsupportedAmount < Spree::Core::GatewayError; end
18
+
19
+ # @param amount [BigDecimal, Spree::Money]
20
+ # @param currency [String]
21
+ # @param payout [Boolean] a payout or transfer rather than a charge
22
+ # @return [Integer]
23
+ # @raise [UnsupportedAmount] when Stripe would refuse the amount
24
+ def to_stripe(amount, currency, payout: false)
25
+ amount = amount.to_d if amount.respond_to?(:to_d)
26
+ code = currency.to_s.upcase
27
+ return Spree::Money::Rounding.quantize(amount, 0).to_i if ZERO_DECIMAL.include?(code)
28
+ return Spree::Money::Rounding.quantize(amount, 0).to_i * 100 if WHOLE_IN_HUNDREDTHS.include?(code)
29
+
30
+ units = Spree::Money::Rounding.to_minor_units(amount, code)
31
+ if THREE_DECIMAL.include?(code) && (units % 10).nonzero?
32
+ raise UnsupportedAmount, "Stripe takes #{code} only in steps of 0.010, got #{amount.to_s('F')}"
33
+ end
34
+ if payout && WHOLE_PAYOUTS.include?(code) && (units % 100).nonzero?
35
+ raise UnsupportedAmount, "Stripe pays out #{code} only in whole units, got #{amount.to_s('F')}"
36
+ end
37
+
38
+ units
39
+ end
40
+
41
+ # @param units [Integer] as Stripe reports it
42
+ # @param currency [String]
43
+ # @return [BigDecimal]
44
+ def from_stripe(units, currency)
45
+ code = currency.to_s.upcase
46
+ return BigDecimal(units) if ZERO_DECIMAL.include?(code)
47
+ return BigDecimal(units) / 100 if WHOLE_IN_HUNDREDTHS.include?(code)
48
+
49
+ Spree::Money::Rounding.from_minor_units(units, code)
50
+ end
51
+ end
52
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spree_stripe
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.0.0.rc1
4
+ version: 6.0.0.rc2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vendo Connect Inc.
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2026-10-08 00:00:00.000000000 Z
12
+ date: 2026-10-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: spree_core
@@ -17,14 +17,14 @@ dependencies:
17
17
  requirements:
18
18
  - - '='
19
19
  - !ruby/object:Gem::Version
20
- version: 6.0.0.rc1
20
+ version: 6.0.0.rc2
21
21
  type: :runtime
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
25
  - - '='
26
26
  - !ruby/object:Gem::Version
27
- version: 6.0.0.rc1
27
+ version: 6.0.0.rc2
28
28
  - !ruby/object:Gem::Dependency
29
29
  name: stripe
30
30
  requirement: !ruby/object:Gem::Requirement
@@ -89,6 +89,7 @@ files:
89
89
  - app/models/spree_stripe/payment_sources/przelewy24.rb
90
90
  - app/models/spree_stripe/payment_sources/sepa_debit.rb
91
91
  - app/models/spree_stripe/payout_provider.rb
92
+ - app/models/spree_stripe/units.rb
92
93
  - app/presenters/spree_stripe/customer_presenter.rb
93
94
  - app/presenters/spree_stripe/statement_descriptor_suffix_presenter.rb
94
95
  - app/services/spree_stripe/create_gateway_webhooks.rb
@@ -110,9 +111,9 @@ licenses:
110
111
  - MIT
111
112
  metadata:
112
113
  bug_tracker_uri: https://github.com/spree/spree/issues
113
- changelog_uri: https://github.com/spree/spree/releases/tag/v6.0.0.rc1
114
+ changelog_uri: https://github.com/spree/spree/releases/tag/v6.0.0.rc2
114
115
  documentation_uri: https://docs.spreecommerce.org/
115
- source_code_uri: https://github.com/spree/spree/tree/v6.0.0.rc1
116
+ source_code_uri: https://github.com/spree/spree/tree/v6.0.0.rc2
116
117
  post_install_message:
117
118
  rdoc_options: []
118
119
  require_paths: