feat: billing brl pix new users (#14617)
## Linear ticket - https://linear.app/chatwoot/issue/CW-7253/billing-brl-pix-new-users ## Description New accounts that sign up in Brazilian Portuguese are now billed in BRL instead of USD. Their Stripe customer is created with a Brazil address and Portuguese locale (so the Stripe portal offers Real prices and PIX), and the AI credit top-up flow shows packages priced in the account's billing currency. Currency support is config-driven, so adding another currency later is a configuration change rather than a code change. ## Type of change - [ ] New feature (non-breaking change which adds functionality) ## How Has This Been Tested? - https://www.loom.com/share/c8d3d08c1b844ed6b820438d4209491a ## Screenshot <img width="904" height="440" alt="image" src="https://github.com/user-attachments/assets/6f19fad8-e6af-46ea-b99f-b0265bb9eeec" /> ## Checklist: - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my code - [ ] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules
This commit is contained in:
@@ -256,6 +256,14 @@ RSpec.describe 'Enterprise Billing APIs', type: :request do
|
||||
{ 'name' => 'Hacker', 'product_id' => ['prod_hacker'], 'price_ids' => ['price_hacker'] },
|
||||
{ 'name' => 'Business', 'product_id' => ['prod_business'], 'price_ids' => ['price_business'] }
|
||||
])
|
||||
create(:installation_config, name: 'CAPTAIN_TOPUP_OPTIONS', value: {
|
||||
'usd' => [
|
||||
{ 'credits' => 1000, 'amount' => 20.0 },
|
||||
{ 'credits' => 2500, 'amount' => 50.0 },
|
||||
{ 'credits' => 6000, 'amount' => 100.0 },
|
||||
{ 'credits' => 12_000, 'amount' => 200.0 }
|
||||
]
|
||||
})
|
||||
end
|
||||
|
||||
it 'returns unauthorized for unauthenticated user' do
|
||||
|
||||
@@ -82,7 +82,8 @@ describe Enterprise::Billing::CreateStripeCustomerService do
|
||||
subscribed_quantity: 2,
|
||||
plan_name: 'A Plan Name',
|
||||
subscription_status: 'active',
|
||||
subscription_ends_on: subscription_ends_on
|
||||
subscription_ends_on: subscription_ends_on,
|
||||
billing_currency: 'usd'
|
||||
}.with_indifferent_access
|
||||
)
|
||||
end
|
||||
@@ -95,7 +96,9 @@ describe Enterprise::Billing::CreateStripeCustomerService do
|
||||
|
||||
create_stripe_customer_service.new(account: account).perform
|
||||
|
||||
expect(Stripe::Customer).to have_received(:create).with({ name: account.name, email: admin1.email })
|
||||
expect(Stripe::Customer).to have_received(:create).with(
|
||||
{ name: account.name, email: admin1.email }
|
||||
)
|
||||
expect(Stripe::Subscription)
|
||||
.to have_received(:create)
|
||||
.with({ customer: customer.id, items: [{ price: 'price_hacker_random', quantity: 2 }] })
|
||||
@@ -108,10 +111,27 @@ describe Enterprise::Billing::CreateStripeCustomerService do
|
||||
subscribed_quantity: 2,
|
||||
plan_name: 'A Plan Name',
|
||||
subscription_status: 'active',
|
||||
subscription_ends_on: subscription_ends_on
|
||||
subscription_ends_on: subscription_ends_on,
|
||||
billing_currency: 'usd'
|
||||
}.with_indifferent_access
|
||||
)
|
||||
end
|
||||
|
||||
it 'sets the billing country override when the account currency requires it' do
|
||||
with_modified_env ENABLE_MULTI_CURRENCY_BILLING: 'true' do
|
||||
account.update!(custom_attributes: { billing_currency: 'brl' })
|
||||
customer = double
|
||||
allow(Stripe::Customer).to receive(:create).and_return(customer)
|
||||
allow(customer).to receive(:id).and_return('cus_random_number')
|
||||
allow(Stripe::Subscription).to receive(:create).and_return(created_subscription)
|
||||
|
||||
create_stripe_customer_service.new(account: account).perform
|
||||
|
||||
expect(Stripe::Customer).to have_received(:create).with(
|
||||
{ name: account.name, email: admin1.email, address: { country: 'BR' }, preferred_locales: ['pt-BR'] }
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when checking for existing subscriptions' do
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe Enterprise::Billing::Currencies do
|
||||
describe 'Brazilian Real (brl)' do
|
||||
it 'is a supported currency' do
|
||||
expect(described_class.supported?('brl')).to be(true)
|
||||
end
|
||||
|
||||
it 'recognizes brl regardless of casing or surrounding whitespace' do
|
||||
expect(described_class.supported?(' BRL ')).to be(true)
|
||||
expect(described_class.normalize(' BRL ')).to eq('brl')
|
||||
end
|
||||
|
||||
it 'keeps brl when coercing to a supported code' do
|
||||
expect(described_class.to_supported('BRL')).to eq('brl')
|
||||
end
|
||||
|
||||
it 'defaults the pt_BR account locale to brl' do
|
||||
expect(described_class.for_locale('pt_BR')).to eq('brl')
|
||||
end
|
||||
|
||||
it 'maps brl to Brazil and the pt-BR checkout locale' do
|
||||
expect(described_class.country_for('brl')).to eq('BR')
|
||||
expect(described_class.preferred_locale_for('brl')).to eq('pt-BR')
|
||||
end
|
||||
|
||||
it 'falls back to the usd default for unsupported input' do
|
||||
expect(described_class.to_supported('eur')).to eq('usd')
|
||||
end
|
||||
|
||||
it 'does not set a country override for usd customers' do
|
||||
expect(described_class.country_for('usd')).to be_nil
|
||||
expect(described_class.preferred_locale_for('usd')).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -15,6 +15,15 @@ describe Enterprise::Billing::TopupCheckoutService do
|
||||
{ 'name' => 'Business', 'product_id' => ['prod_business'], 'price_ids' => ['price_business'] }
|
||||
])
|
||||
|
||||
create(:installation_config, name: 'CAPTAIN_TOPUP_OPTIONS', value: {
|
||||
'usd' => [
|
||||
{ 'credits' => 1000, 'amount' => 20.0 },
|
||||
{ 'credits' => 2500, 'amount' => 50.0 },
|
||||
{ 'credits' => 6000, 'amount' => 100.0 },
|
||||
{ 'credits' => 12_000, 'amount' => 200.0 }
|
||||
]
|
||||
})
|
||||
|
||||
account.update!(
|
||||
custom_attributes: { plan_name: 'Business', stripe_customer_id: stripe_customer_id },
|
||||
limits: { 'captain_responses' => 500 }
|
||||
|
||||
Reference in New Issue
Block a user