From 32d2033de50c395c7b418fde2a7a1a730ee8aa9a Mon Sep 17 00:00:00 2001 From: rrader2890 Date: Tue, 16 Jun 2026 20:25:15 -0400 Subject: [PATCH] fix(white-label): actually create the Stripe customer so accept goes straight to payment Root cause: provisioning set payment_provider: stripe but did not pass provider_customer.sync_with_provider, so Lago never created the Stripe customer (create_billing_configuration returns early). GenerateCheckoutUrlService then failed with no_linked_payment_provider and the gate showed a dead-end "we'll email you" message. - flobyte:provision_whitelabel now passes provider_customer: {sync_with_provider: true, sync: true} so the Stripe customer is created up front. - accept action self-heals (ensure_provider_customer) before generating checkout, fixing customers provisioned before this change. - Replace the "we'll email you" fallback with an in-platform redirect to the customer portal (no email; it's all one platform). Co-Authored-By: Claude Opus 4.8 (1M context) --- app/controllers/white_label_controller.rb | 43 +++++++++++++++++++---- lib/tasks/flobyte.rake | 6 +++- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/app/controllers/white_label_controller.rb b/app/controllers/white_label_controller.rb index 32b43eb83a04..5639b423e208 100644 --- a/app/controllers/white_label_controller.rb +++ b/app/controllers/white_label_controller.rb @@ -18,6 +18,8 @@ # below are intentionally disabled for this trusted-markup controller. # rubocop:disable Rails/ApplicationController, Rails/OutputSafety class WhiteLabelController < ActionController::Base + include Customers::PaymentProviderFinder + skip_forgery_protection def show @@ -43,13 +45,17 @@ def accept user_agent: request.user_agent ) + # Provisioning may have only recorded which provider to use without creating + # the Stripe customer; create it now (idempotent) so checkout can generate. + ensure_provider_customer(agreement.customer) + checkout = ::Customers::GenerateCheckoutUrlService.call(customer: agreement.customer) if checkout.success? && checkout.checkout_url.present? redirect_to checkout.checkout_url, allow_other_host: true else - # Card setup not available yet (e.g. Stripe customer still provisioning). - # Consent is recorded; payment instructions will follow by email. - render_accepted_without_card + # Checkout couldn't be generated — keep them in-platform: send them to the + # customer portal where they can add a payment method and pay. + redirect_to_portal(agreement.customer) end end @@ -92,10 +98,33 @@ def render_status(agreement) render html: layout("

White-Label / SDK Agreement

#{msg}

").html_safe end - def render_accepted_without_card - body = '

Thank you

Your acceptance has been recorded. ' \ - "We will email you a secure link to add your payment method shortly.

" - render html: layout(body).html_safe + # Lago records *which* provider to use at customer-create but may not create + # the Stripe customer; do it now so GenerateCheckoutUrlService can succeed. + def ensure_provider_customer(customer) + return if customer.payment_provider.blank? + return if payment_provider_customer(customer)&.provider_customer_id.present? + + provider = payment_provider(customer) + return if provider.nil? + + PaymentProviders::CreateCustomerFactory.new_instance( + provider: customer.payment_provider, + customer:, + payment_provider_id: provider.id, + params: {sync_with_provider: true}, + async: false + ).call + rescue => e + Rails.logger.error("[white-label] ensure_provider_customer failed: #{e.message}") + end + + # In-platform fallback if checkout can't be generated: the no-login customer + # portal, where they can add a payment method and pay. No email involved. + def redirect_to_portal(customer) + url = ::CustomerPortal::GenerateUrlService.call(customer:).url + return render_invalid if url.blank? + + redirect_to url, allow_other_host: true end def render_invalid diff --git a/lib/tasks/flobyte.rake b/lib/tasks/flobyte.rake index 5e856de85a8c..0c93e931b0d9 100644 --- a/lib/tasks/flobyte.rake +++ b/lib/tasks/flobyte.rake @@ -200,7 +200,11 @@ module FlobyteCatalog email:, currency: "USD", payment_provider: "stripe", - payment_provider_code: stripe.code + payment_provider_code: stripe.code, + # Eagerly create the Stripe customer (sync) so the acceptance gate can + # generate a checkout URL immediately — without this, Lago only records + # *which* provider to use and never creates the Stripe customer. + provider_customer: {sync_with_provider: true, sync: true} ) result.raise_if_error! customer = result.customer