Module: BetterAuth::Plugins

Defined in:
lib/better_auth/plugins/stripe.rb

Constant Summary collapse

STRIPE_ERROR_CODES =
BetterAuth::Stripe::ERROR_CODES
STRIPE_UNSAFE_METADATA_KEYS =
BetterAuth::Stripe::Metadata::UNSAFE_KEYS

Class Method Summary collapse

Class Method Details

.stripe(options = {}) ⇒ Object



14
15
16
# File 'lib/better_auth/plugins/stripe.rb', line 14

def stripe(options = {})
  BetterAuth::Stripe::PluginFactory.build(options)
end

.stripe_active_or_trialing?(subscription) ⇒ Boolean

Returns:

  • (Boolean)


251
252
253
# File 'lib/better_auth/plugins/stripe.rb', line 251

def stripe_active_or_trialing?(subscription)
  BetterAuth::Stripe::Utils.active_or_trialing?(subscription)
end

.stripe_active_subscription(ctx, reference_id) ⇒ Object



242
243
244
# File 'lib/better_auth/plugins/stripe.rb', line 242

def stripe_active_subscription(ctx, reference_id)
  ctx.context.adapter.find_many(model: "subscription", where: [{field: "referenceId", value: reference_id}]).find { |entry| stripe_active_or_trialing?(entry) }
end

.stripe_active_subscriptions(config, customer_id) ⇒ Object



246
247
248
249
# File 'lib/better_auth/plugins/stripe.rb', line 246

def stripe_active_subscriptions(config, customer_id)
  result = stripe_client(config).subscriptions.list(customer: customer_id)
  Array(stripe_fetch(result, "data")).select { |entry| stripe_active_or_trialing?(entry) }
end

.stripe_authorize_reference!(ctx, session, reference_id, action, customer_type, subscription_options, explicit: false) ⇒ Object



195
196
197
# File 'lib/better_auth/plugins/stripe.rb', line 195

def stripe_authorize_reference!(ctx, session, reference_id, action, customer_type, subscription_options, explicit: false)
  BetterAuth::Stripe::Middleware.authorize_reference!(ctx, session, reference_id, action, customer_type, subscription_options, explicit: explicit)
end

.stripe_billing_portal_endpoint(config) ⇒ Object



50
51
52
# File 'lib/better_auth/plugins/stripe.rb', line 50

def stripe_billing_portal_endpoint(config)
  BetterAuth::Stripe::Routes::CreateBillingPortal.endpoint(config)
end

.stripe_cancel_callback_endpoint(config) ⇒ Object



54
55
56
# File 'lib/better_auth/plugins/stripe.rb', line 54

def stripe_cancel_callback_endpoint(config)
  BetterAuth::Stripe::Routes::CancelSubscriptionCallback.endpoint(config)
end

.stripe_cancel_subscription_endpoint(config) ⇒ Object



38
39
40
# File 'lib/better_auth/plugins/stripe.rb', line 38

def stripe_cancel_subscription_endpoint(config)
  BetterAuth::Stripe::Routes::CancelSubscription.endpoint(config)
end

.stripe_checkout_line_items(config, plan, price_id, quantity, auto_managed_seats, seat_only_plan) ⇒ Object



279
280
281
# File 'lib/better_auth/plugins/stripe.rb', line 279

def stripe_checkout_line_items(config, plan, price_id, quantity, auto_managed_seats, seat_only_plan)
  BetterAuth::Stripe::Utils.checkout_line_items(config, plan, price_id, quantity, auto_managed_seats, seat_only_plan)
end

.stripe_client(config) ⇒ Object

Raises:

  • (APIError)


144
145
146
147
148
149
150
151
152
153
# File 'lib/better_auth/plugins/stripe.rb', line 144

def stripe_client(config)
  injected = config[:stripe_client] || config[:client]
  return injected if injected
  return config[:_stripe_client_adapter] if config[:_stripe_client_adapter]

  api_key = config[:stripe_api_key] || ENV["STRIPE_SECRET_KEY"]
  raise APIError.new("INTERNAL_SERVER_ERROR", message: "Stripe client is required") if api_key.to_s.empty?

  config[:_stripe_client_adapter] = BetterAuth::Stripe::ClientAdapter.new(api_key)
end

.stripe_consume_positive_delta(line_item_delta, price) ⇒ Object



306
307
308
309
310
311
# File 'lib/better_auth/plugins/stripe.rb', line 306

def stripe_consume_positive_delta(line_item_delta, price)
  delta = line_item_delta[price]
  return unless delta&.positive?

  (delta == 1) ? line_item_delta.delete(price) : line_item_delta[price] = delta - 1
end

.stripe_create_customer(config, ctx, user, metadata = nil) ⇒ Object



86
87
88
89
90
91
# File 'lib/better_auth/plugins/stripe.rb', line 86

def stripe_create_customer(config, ctx, user,  = nil)
  customer = stripe_find_or_create_user_customer(config, user, , ctx)
  id = stripe_id(customer)
  ctx.context.internal_adapter.update_user(user.fetch("id"), stripeCustomerId: id)
  id
end

.stripe_customer_metadata_get(metadata) ⇒ Object



485
486
487
# File 'lib/better_auth/plugins/stripe.rb', line 485

def ()
  BetterAuth::Stripe::Metadata.customer_get()
end

.stripe_customer_metadata_set(internal_fields, *user_metadata) ⇒ Object



481
482
483
# File 'lib/better_auth/plugins/stripe.rb', line 481

def (internal_fields, *)
  BetterAuth::Stripe::Metadata.customer_set(internal_fields, *)
end

.stripe_customer_type!(source) ⇒ Object



199
200
201
# File 'lib/better_auth/plugins/stripe.rb', line 199

def stripe_customer_type!(source)
  BetterAuth::Stripe::Middleware.customer_type!(source)
end

.stripe_database_hooks(config) ⇒ Object



26
27
28
# File 'lib/better_auth/plugins/stripe.rb', line 26

def stripe_database_hooks(config)
  BetterAuth::Stripe::PluginFactory.database_hooks(config)
end

.stripe_deep_merge(base, override) ⇒ Object



516
517
518
# File 'lib/better_auth/plugins/stripe.rb', line 516

def stripe_deep_merge(base, override)
  BetterAuth::Stripe::Metadata.deep_merge(base, override)
end

.stripe_direct_subscription_update?(old_plan, plan, auto_managed_seats) ⇒ Boolean

Returns:

  • (Boolean)


409
410
411
# File 'lib/better_auth/plugins/stripe.rb', line 409

def stripe_direct_subscription_update?(old_plan, plan, auto_managed_seats)
  BetterAuth::Stripe::Utils.direct_subscription_update?(old_plan, plan, auto_managed_seats)
end

.stripe_endpoints(config) ⇒ Object



22
23
24
# File 'lib/better_auth/plugins/stripe.rb', line 22

def stripe_endpoints(config)
  BetterAuth::Stripe::Routes.endpoints(config)
end

.stripe_escape_search(value) ⇒ Object



532
533
534
# File 'lib/better_auth/plugins/stripe.rb', line 532

def stripe_escape_search(value)
  BetterAuth::Stripe::Utils.escape_search(value)
end

.stripe_fetch(object, key) ⇒ Object



159
160
161
# File 'lib/better_auth/plugins/stripe.rb', line 159

def stripe_fetch(object, key)
  BetterAuth::Stripe::Utils.fetch(object, key)
end

.stripe_find_or_create_user_customer(config, user, metadata = nil, ctx = nil) ⇒ Object



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/better_auth/plugins/stripe.rb', line 93

def stripe_find_or_create_user_customer(config, user,  = nil, ctx = nil)
  customer = stripe_find_user_customer(config, user["email"])
  if customer
    stripe_notify_customer_created(config, customer, user, ctx)
    return customer
  end

  raw_extra = config[:get_customer_create_params]&.call(user, ctx) || {}
   = stripe_fetch(raw_extra, "metadata")
  extra = normalize_hash(raw_extra)
  params = stripe_deep_merge(
    extra,
    email: user["email"],
    name: user["name"],
    metadata: ({userId: user["id"], customerType: "user"}, , )
  )
  params[:metadata] = ({userId: user["id"], customerType: "user"}, , )
  customer = stripe_client(config).customers.create(params)
  stripe_notify_customer_created(config, customer, user, ctx)
  customer
end

.stripe_find_organization_customer(config, organization_id) ⇒ Object



216
217
218
219
220
221
222
223
224
225
226
227
228
229
# File 'lib/better_auth/plugins/stripe.rb', line 216

def stripe_find_organization_customer(config, organization_id)
  customers = stripe_client(config).customers
  begin
    existing = customers.search(query: "metadata[\"organizationId\"]:\"#{stripe_escape_search(organization_id)}\" AND metadata[\"customerType\"]:\"organization\"", limit: 1)
    Array(stripe_fetch(existing, "data")).first
  rescue
    listed = customers.list(limit: 100)
    Array(stripe_fetch(listed, "data")).find do |customer|
       = stripe_fetch(customer, "metadata") || {}
      (, "organizationId") == organization_id &&
        (, "customerType") == "organization"
    end
  end
end

.stripe_find_subscription_for_action(ctx, reference_id, subscription_id, active_only:) ⇒ Object



231
232
233
234
235
236
237
238
239
240
# File 'lib/better_auth/plugins/stripe.rb', line 231

def stripe_find_subscription_for_action(ctx, reference_id, subscription_id, active_only:)
  subscription = if subscription_id
    ctx.context.adapter.find_one(model: "subscription", where: [{field: "stripeSubscriptionId", value: subscription_id}])
  else
    ctx.context.adapter.find_many(model: "subscription", where: [{field: "referenceId", value: reference_id}]).find { |entry| !active_only || stripe_active_or_trialing?(entry) }
  end
  return nil if subscription_id && subscription && subscription["referenceId"] != reference_id

  subscription
end

.stripe_find_user_customer(config, email) ⇒ Object



203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/better_auth/plugins/stripe.rb', line 203

def stripe_find_user_customer(config, email)
  customers = stripe_client(config).customers
  begin
    existing = customers.search(query: "email:\"#{stripe_escape_search(email)}\" AND -metadata[\"customerType\"]:\"organization\"", limit: 1)
    Array(stripe_fetch(existing, "data")).first
  rescue
    listed = customers.list(email: email, limit: 100)
    Array(stripe_fetch(listed, "data")).find do |customer|
      (stripe_fetch(customer, "metadata") || {}, "customerType") != "organization"
    end
  end
end

.stripe_handle_event(ctx, event) ⇒ Object



66
67
68
# File 'lib/better_auth/plugins/stripe.rb', line 66

def stripe_handle_event(ctx, event)
  BetterAuth::Stripe::Hooks.handle_event(ctx, event)
end

.stripe_id(object) ⇒ Object



155
156
157
# File 'lib/better_auth/plugins/stripe.rb', line 155

def stripe_id(object)
  BetterAuth::Stripe::Utils.id(object)
end

.stripe_line_item(config, price_id, quantity) ⇒ Object



275
276
277
# File 'lib/better_auth/plugins/stripe.rb', line 275

def stripe_line_item(config, price_id, quantity)
  BetterAuth::Stripe::Utils.line_item(config, price_id, quantity)
end

.stripe_line_item_delta(old_plan, plan) ⇒ Object



287
288
289
290
291
292
293
294
295
296
297
298
# File 'lib/better_auth/plugins/stripe.rb', line 287

def stripe_line_item_delta(old_plan, plan)
  delta = Hash.new(0)
  stripe_plan_line_items(old_plan || {}).each do |item|
    price = stripe_fetch(item, "price")
    delta[price] -= 1 if price
  end
  stripe_plan_line_items(plan || {}).each do |item|
    price = stripe_fetch(item, "price")
    delta[price] += 1 if price
  end
  delta.delete_if { |_price, count| count == 0 }
end

.stripe_list_subscriptions_endpoint(config) ⇒ Object



46
47
48
# File 'lib/better_auth/plugins/stripe.rb', line 46

def stripe_list_subscriptions_endpoint(config)
  BetterAuth::Stripe::Routes::ListActiveSubscriptions.endpoint(config)
end

.stripe_metadata(internal, *user_metadata) ⇒ Object



477
478
479
# File 'lib/better_auth/plugins/stripe.rb', line 477

def (internal, *)
  BetterAuth::Stripe::Metadata.merge(internal, *)
end

.stripe_metadata_fetch(metadata, key) ⇒ Object



512
513
514
# File 'lib/better_auth/plugins/stripe.rb', line 512

def (, key)
  BetterAuth::Stripe::Metadata.(, key)
end

.stripe_metadata_key(key) ⇒ Object



508
509
510
# File 'lib/better_auth/plugins/stripe.rb', line 508

def (key)
  BetterAuth::Stripe::Metadata.(key)
end

.stripe_metered_price?(config, price_id, lookup_key = nil) ⇒ Boolean

Returns:

  • (Boolean)


457
458
459
# File 'lib/better_auth/plugins/stripe.rb', line 457

def stripe_metered_price?(config, price_id, lookup_key = nil)
  BetterAuth::Stripe::Utils.metered_price?(config, price_id, lookup_key)
end

.stripe_notify_customer_created(config, customer, user, ctx) ⇒ Object



497
498
499
500
501
502
503
504
505
506
# File 'lib/better_auth/plugins/stripe.rb', line 497

def stripe_notify_customer_created(config, customer, user, ctx)
  config[:on_customer_create]&.call(
    {
      stripeCustomer: customer,
      stripe_customer: customer,
      user: user.merge("stripeCustomerId" => stripe_id(customer))
    },
    ctx
  )
end

.stripe_on_checkout_completed(ctx, event) ⇒ Object



70
71
72
# File 'lib/better_auth/plugins/stripe.rb', line 70

def stripe_on_checkout_completed(ctx, event)
  BetterAuth::Stripe::Hooks.on_checkout_completed(ctx, event)
end

.stripe_on_subscription_created(ctx, event) ⇒ Object



74
75
76
# File 'lib/better_auth/plugins/stripe.rb', line 74

def stripe_on_subscription_created(ctx, event)
  BetterAuth::Stripe::Hooks.on_subscription_created(ctx, event)
end

.stripe_on_subscription_deleted(ctx, event) ⇒ Object



82
83
84
# File 'lib/better_auth/plugins/stripe.rb', line 82

def stripe_on_subscription_deleted(ctx, event)
  BetterAuth::Stripe::Hooks.on_subscription_deleted(ctx, event)
end

.stripe_on_subscription_updated(ctx, event) ⇒ Object



78
79
80
# File 'lib/better_auth/plugins/stripe.rb', line 78

def stripe_on_subscription_updated(ctx, event)
  BetterAuth::Stripe::Hooks.on_subscription_updated(ctx, event)
end

.stripe_organization_customer(config, ctx, organization_id, metadata = nil) ⇒ Object



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/better_auth/plugins/stripe.rb', line 115

def stripe_organization_customer(config, ctx, organization_id,  = nil)
  raise APIError.new("BAD_REQUEST", message: "Organization integration requires the organization plugin") unless config.dig(:organization, :enabled)

  org = ctx.context.adapter.find_one(model: "organization", where: [{field: "id", value: organization_id}])
  raise APIError.new("BAD_REQUEST", message: STRIPE_ERROR_CODES.fetch("ORGANIZATION_NOT_FOUND")) unless org
  return org["stripeCustomerId"] if org["stripeCustomerId"]

  customer = stripe_find_organization_customer(config, org["id"])
  unless customer
    raw_extra = config.dig(:organization, :get_customer_create_params)&.call(org, ctx) || {}
     = stripe_fetch(raw_extra, "metadata")
    extra = normalize_hash(raw_extra)
    params = stripe_deep_merge(
      extra,
      name: org["name"],
      metadata: ({organizationId: org["id"], customerType: "organization"}, , )
    )
    params[:metadata] = ({organizationId: org["id"], customerType: "organization"}, , )
    customer = stripe_client(config).customers.create(params)
    config.dig(:organization, :on_customer_create)&.call({stripeCustomer: customer, stripe_customer: customer, organization: org.merge("stripeCustomerId" => stripe_id(customer))}, ctx)
  end
  ctx.context.adapter.update(model: "organization", where: [{field: "id", value: org.fetch("id")}], update: {stripeCustomerId: stripe_id(customer)})
  stripe_id(customer)
rescue APIError
  raise
rescue
  raise APIError.new("BAD_REQUEST", message: STRIPE_ERROR_CODES.fetch("UNABLE_TO_CREATE_CUSTOMER"))
end

.stripe_organization_hooks(config) ⇒ Object



30
31
32
# File 'lib/better_auth/plugins/stripe.rb', line 30

def stripe_organization_hooks(config)
  BetterAuth::Stripe::OrganizationHooks.hooks(config)
end

.stripe_pending_cancel?(subscription) ⇒ Boolean

Returns:

  • (Boolean)


255
256
257
# File 'lib/better_auth/plugins/stripe.rb', line 255

def stripe_pending_cancel?(subscription)
  BetterAuth::Stripe::Utils.pending_cancel?(subscription)
end

.stripe_plan_by_name(config, name) ⇒ Object



175
176
177
# File 'lib/better_auth/plugins/stripe.rb', line 175

def stripe_plan_by_name(config, name)
  BetterAuth::Stripe::Utils.plan_by_name(config, name)
end

.stripe_plan_by_price_info(config, price_id, lookup_key = nil) ⇒ Object



179
180
181
# File 'lib/better_auth/plugins/stripe.rb', line 179

def stripe_plan_by_price_info(config, price_id, lookup_key = nil)
  BetterAuth::Stripe::Utils.plan_by_price_info(config, price_id, lookup_key)
end

.stripe_plan_line_items(plan) ⇒ Object



283
284
285
# File 'lib/better_auth/plugins/stripe.rb', line 283

def stripe_plan_line_items(plan)
  BetterAuth::Stripe::Utils.plan_line_items(plan)
end

.stripe_plans(config) ⇒ Object



171
172
173
# File 'lib/better_auth/plugins/stripe.rb', line 171

def stripe_plans(config)
  BetterAuth::Stripe::Utils.plans(config)
end

.stripe_price_id(config, plan, annual = false) ⇒ Object



183
184
185
# File 'lib/better_auth/plugins/stripe.rb', line 183

def stripe_price_id(config, plan, annual = false)
  BetterAuth::Stripe::Utils.price_id(config, plan, annual)
end

.stripe_redirect?(body) ⇒ Boolean

Returns:

  • (Boolean)


520
521
522
# File 'lib/better_auth/plugins/stripe.rb', line 520

def stripe_redirect?(body)
  BetterAuth::Stripe::Utils.redirect?(body)
end

.stripe_reference_by_customer(ctx, config, customer_id) ⇒ Object



473
474
475
# File 'lib/better_auth/plugins/stripe.rb', line 473

def stripe_reference_by_customer(ctx, config, customer_id)
  BetterAuth::Stripe::Middleware.reference_by_customer(ctx, config, customer_id)
end

.stripe_reference_id!(ctx, session, customer_type, explicit_reference_id, config) ⇒ Object



191
192
193
# File 'lib/better_auth/plugins/stripe.rb', line 191

def stripe_reference_id!(ctx, session, customer_type, explicit_reference_id, config)
  BetterAuth::Stripe::Middleware.reference_id!(ctx, session, customer_type, explicit_reference_id, config)
end

.stripe_release_plugin_schedule(ctx, config, customer_id, active_stripe, db_subscription) ⇒ Object



387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
# File 'lib/better_auth/plugins/stripe.rb', line 387

def stripe_release_plugin_schedule(ctx, config, customer_id, active_stripe, db_subscription)
  return unless stripe_schedule_id(active_stripe)
  return unless stripe_client(config).respond_to?(:subscription_schedules)

  schedules = stripe_client(config).subscription_schedules.list(customer: customer_id)
  active_subscription_id = stripe_fetch(active_stripe, "id")
  existing = Array(stripe_fetch(schedules, "data")).find do |schedule|
    subscription = stripe_fetch(schedule, "subscription")
    schedule_subscription_id = subscription.is_a?(Hash) ? stripe_id(subscription) : subscription
     = stripe_fetch(schedule, "metadata") || {}
    schedule_subscription_id == active_subscription_id &&
      stripe_fetch(schedule, "status") == "active" &&
      (, "source") == "@better-auth/stripe"
  end
  return unless existing

  stripe_client(config).subscription_schedules.release(stripe_id(existing))
  if db_subscription
    ctx.context.adapter.update(model: "subscription", where: [{field: "id", value: db_subscription.fetch("id")}], update: {stripeScheduleId: nil})
  end
end

.stripe_remove_quota(line_item_delta) ⇒ Object



300
301
302
303
304
# File 'lib/better_auth/plugins/stripe.rb', line 300

def stripe_remove_quota(line_item_delta)
  line_item_delta.each_with_object({}) do |(price, delta), result|
    result[price] = -delta if delta.negative?
  end
end

.stripe_resolve_lookup(config, lookup_key) ⇒ Object



187
188
189
# File 'lib/better_auth/plugins/stripe.rb', line 187

def stripe_resolve_lookup(config, lookup_key)
  BetterAuth::Stripe::Utils.resolve_lookup(config, lookup_key)
end

.stripe_resolve_plan_item(config, subscription) ⇒ Object



267
268
269
# File 'lib/better_auth/plugins/stripe.rb', line 267

def stripe_resolve_plan_item(config, subscription)
  BetterAuth::Stripe::Utils.resolve_plan_item(config, subscription)
end

.stripe_resolve_quantity(subscription, plan_item, plan = nil) ⇒ Object



271
272
273
# File 'lib/better_auth/plugins/stripe.rb', line 271

def stripe_resolve_quantity(subscription, plan_item, plan = nil)
  BetterAuth::Stripe::Utils.resolve_quantity(subscription, plan_item, plan)
end

.stripe_resolve_stripe_price(config, price_id, lookup_key = nil) ⇒ Object



461
462
463
# File 'lib/better_auth/plugins/stripe.rb', line 461

def stripe_resolve_stripe_price(config, price_id, lookup_key = nil)
  BetterAuth::Stripe::Utils.resolve_stripe_price(config, price_id, lookup_key)
end

.stripe_restore_subscription_endpoint(config) ⇒ Object



42
43
44
# File 'lib/better_auth/plugins/stripe.rb', line 42

def stripe_restore_subscription_endpoint(config)
  BetterAuth::Stripe::Routes::RestoreSubscription.endpoint(config)
end

.stripe_schedule_id(subscription) ⇒ Object



469
470
471
# File 'lib/better_auth/plugins/stripe.rb', line 469

def stripe_schedule_id(subscription)
  BetterAuth::Stripe::Utils.schedule_id(subscription)
end

.stripe_schedule_plan_change(ctx, config, active_stripe, db_subscription, plan, price_id, quantity, seat_only_plan, body) ⇒ Object



313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
# File 'lib/better_auth/plugins/stripe.rb', line 313

def stripe_schedule_plan_change(ctx, config, active_stripe, db_subscription, plan, price_id, quantity, seat_only_plan, body)
  schedule = stripe_client(config).subscription_schedules.create(from_subscription: stripe_fetch(active_stripe, "id"))
  current_phase = Array(stripe_fetch(schedule, "phases")).first || {}
  current_items = Array(stripe_fetch(current_phase, "items"))
  active_item = stripe_resolve_plan_item(config, active_stripe)&.fetch(:item, nil) || stripe_subscription_item(active_stripe)
  active_price_id = stripe_fetch(stripe_fetch(active_item || {}, "price") || {}, "id")
  active_price_present = current_items.any? do |item|
    item_price = stripe_fetch(item, "price")
    item_price = stripe_fetch(item_price, "id") if item_price.is_a?(Hash)
    item_price == active_price_id
  end
  old_plan = db_subscription && stripe_plan_by_name(config, db_subscription["plan"])
  line_item_delta = stripe_line_item_delta(old_plan, plan)
  remove_quota = stripe_remove_quota(line_item_delta)
  price_map = {}
  if plan[:seat_price_id] && old_plan && old_plan[:seat_price_id] && old_plan[:seat_price_id] != plan[:seat_price_id]
    price_map[old_plan[:seat_price_id]] = {price: plan[:seat_price_id], quantity: quantity}
  end
  new_items = current_items.filter_map do |item|
    item_price = stripe_fetch(item, "price")
    item_price = stripe_fetch(item_price, "id") if item_price.is_a?(Hash)
    quota = remove_quota[item_price].to_i
    if quota.positive?
      remove_quota[item_price] = quota - 1
      next nil
    end

    replacement = price_map[item_price]
    if replacement
      next {price: replacement[:price], quantity: replacement[:quantity] || stripe_fetch(item, "quantity")}.compact
    end

    if item_price == active_price_id
      next nil if seat_only_plan

      stripe_line_item(config, price_id, plan[:seat_price_id] ? 1 : quantity)
    else
      stripe_consume_positive_delta(line_item_delta, item_price)
      {price: item_price, quantity: stripe_fetch(item, "quantity")}.compact
    end
  end
  new_items << stripe_line_item(config, price_id, plan[:seat_price_id] ? 1 : quantity) unless active_price_present || seat_only_plan
  new_items << {price: plan[:seat_price_id], quantity: quantity} if plan[:seat_price_id] && !new_items.any? { |item| item[:price] == plan[:seat_price_id] }
  line_item_delta.each do |price, delta|
    delta.times { new_items << {price: price} } if delta.positive?
  end

  stripe_client(config).subscription_schedules.update(
    stripe_fetch(schedule, "id"),
    metadata: {source: "@better-auth/stripe"},
    end_behavior: "release",
    phases: [
      {
        items: current_items.map do |item|
          item_price = stripe_fetch(item, "price")
          item_price = stripe_fetch(item_price, "id") if item_price.is_a?(Hash)
          {price: item_price, quantity: stripe_fetch(item, "quantity")}.compact
        end,
        start_date: stripe_fetch(current_phase, "start_date"),
        end_date: stripe_fetch(current_phase, "end_date")
      },
      {
        items: new_items,
        start_date: stripe_fetch(current_phase, "end_date"),
        proration_behavior: "none"
      }
    ]
  )
  if db_subscription
    ctx.context.adapter.update(model: "subscription", where: [{field: "id", value: db_subscription.fetch("id")}], update: {stripeScheduleId: stripe_fetch(schedule, "id")})
  end
  stripe_url(ctx, body[:return_url] || "/")
end

.stripe_schema(config) ⇒ Object



18
19
20
# File 'lib/better_auth/plugins/stripe.rb', line 18

def stripe_schema(config)
  BetterAuth::Stripe::Schema.schema(config)
end

.stripe_stringify_keys(value) ⇒ Object



524
525
526
# File 'lib/better_auth/plugins/stripe.rb', line 524

def stripe_stringify_keys(value)
  BetterAuth::Stripe::Metadata.stringify_keys(value)
end

.stripe_stripe_pending_cancel?(subscription) ⇒ Boolean

Returns:

  • (Boolean)


259
260
261
# File 'lib/better_auth/plugins/stripe.rb', line 259

def stripe_stripe_pending_cancel?(subscription)
  BetterAuth::Stripe::Utils.stripe_pending_cancel?(subscription)
end

.stripe_subscription_item(subscription) ⇒ Object



263
264
265
# File 'lib/better_auth/plugins/stripe.rb', line 263

def stripe_subscription_item(subscription)
  BetterAuth::Stripe::Utils.subscription_item(subscription)
end

.stripe_subscription_metadata_get(metadata) ⇒ Object



493
494
495
# File 'lib/better_auth/plugins/stripe.rb', line 493

def ()
  BetterAuth::Stripe::Metadata.subscription_get()
end

.stripe_subscription_metadata_set(internal_fields, *user_metadata) ⇒ Object



489
490
491
# File 'lib/better_auth/plugins/stripe.rb', line 489

def (internal_fields, *)
  BetterAuth::Stripe::Metadata.subscription_set(internal_fields, *)
end

.stripe_subscription_options(config) ⇒ Object



167
168
169
# File 'lib/better_auth/plugins/stripe.rb', line 167

def stripe_subscription_options(config)
  BetterAuth::Stripe::Utils.subscription_options(config)
end

.stripe_subscription_state(subscription, include_status: true, compact: true) ⇒ Object



465
466
467
# File 'lib/better_auth/plugins/stripe.rb', line 465

def stripe_subscription_state(subscription, include_status: true, compact: true)
  BetterAuth::Stripe::Utils.subscription_state(subscription, include_status: include_status, compact: compact)
end

.stripe_success_endpoint(config) ⇒ Object



58
59
60
# File 'lib/better_auth/plugins/stripe.rb', line 58

def stripe_success_endpoint(config)
  BetterAuth::Stripe::Routes::SubscriptionSuccess.endpoint(config)
end

.stripe_sync_organization_seats(config, data, ctx) ⇒ Object



453
454
455
# File 'lib/better_auth/plugins/stripe.rb', line 453

def stripe_sync_organization_seats(config, data, ctx)
  BetterAuth::Stripe::OrganizationHooks.sync_seats(config, data, ctx)
end

.stripe_time(value) ⇒ Object



163
164
165
# File 'lib/better_auth/plugins/stripe.rb', line 163

def stripe_time(value)
  BetterAuth::Stripe::Utils.time(value)
end

.stripe_update_active_subscription_items(ctx, config, active_stripe, db_subscription, old_plan, plan, price_id, quantity, seat_only_plan, body) ⇒ Object



413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
# File 'lib/better_auth/plugins/stripe.rb', line 413

def stripe_update_active_subscription_items(ctx, config, active_stripe, db_subscription, old_plan, plan, price_id, quantity, seat_only_plan, body)
  active_item = stripe_resolve_plan_item(config, active_stripe)&.fetch(:item, nil) || stripe_subscription_item(active_stripe)
  active_price_id = stripe_fetch(stripe_fetch(active_item || {}, "price") || {}, "id")
  line_item_delta = stripe_line_item_delta(old_plan, plan)
  remove_quota = stripe_remove_quota(line_item_delta)
  price_map = {}
  if plan[:seat_price_id] && old_plan && old_plan[:seat_price_id] && old_plan[:seat_price_id] != plan[:seat_price_id]
    price_map[old_plan[:seat_price_id]] = {price: plan[:seat_price_id], quantity: quantity}
  end
  items = []
  Array(stripe_fetch(stripe_fetch(active_stripe, "items") || {}, "data")).each do |item|
    item_price = stripe_fetch(stripe_fetch(item, "price") || {}, "id")
    quota = remove_quota[item_price].to_i
    if quota.positive?
      remove_quota[item_price] = quota - 1
      items << {id: stripe_fetch(item, "id"), deleted: true}
    elsif price_map[item_price]
      replacement = price_map[item_price]
      items << {id: stripe_fetch(item, "id"), price: replacement[:price], quantity: replacement[:quantity]}
    elsif item_price == active_price_id
      items << stripe_line_item(config, price_id, plan[:seat_price_id] ? 1 : quantity).merge(id: stripe_fetch(item, "id")) unless seat_only_plan
    else
      stripe_consume_positive_delta(line_item_delta, item_price)
    end
  end
  items << {price: plan[:seat_price_id], quantity: quantity} if plan[:seat_price_id] && !items.any? { |item| item[:price] == plan[:seat_price_id] || item[:id] && item[:price] == plan[:seat_price_id] }
  line_item_delta.each do |price, delta|
    delta.times { items << {price: price} } if delta.positive?
  end
  stripe_client(config).subscriptions.update(stripe_fetch(active_stripe, "id"), items: items, proration_behavior: plan[:proration_behavior] || "create_prorations")
  if db_subscription
    ctx.context.adapter.update(
      model: "subscription",
      where: [{field: "id", value: db_subscription.fetch("id")}],
      update: {plan: plan[:name].to_s.downcase, seats: quantity, limits: plan[:limits], stripeScheduleId: nil}
    )
  end
  stripe_url(ctx, body[:return_url] || "/")
end

.stripe_upgrade_subscription_endpoint(config) ⇒ Object



34
35
36
# File 'lib/better_auth/plugins/stripe.rb', line 34

def stripe_upgrade_subscription_endpoint(config)
  BetterAuth::Stripe::Routes::UpgradeSubscription.endpoint(config)
end

.stripe_url(ctx, url) ⇒ Object



528
529
530
# File 'lib/better_auth/plugins/stripe.rb', line 528

def stripe_url(ctx, url)
  BetterAuth::Stripe::Utils.url(ctx, url)
end

.stripe_webhook_endpoint(config) ⇒ Object



62
63
64
# File 'lib/better_auth/plugins/stripe.rb', line 62

def stripe_webhook_endpoint(config)
  BetterAuth::Stripe::Routes::StripeWebhook.endpoint(config)
end