Commit d6e31b67 authored by Dmytro Zaporozhets's avatar Dmytro Zaporozhets

Merge branch 'growth-87-confirm-order-backend' into 'master'

API endpoint for confirm order component for paid signup flow

Closes gitlab-org/growth/engineering#55, gitlab-org/growth/engineering#54, and gitlab-org/growth/engineering#52

See merge request gitlab-org/gitlab!21736
parents f3544845 7aeddf7b
......@@ -43,8 +43,33 @@ class SubscriptionsController < ApplicationController
render json: response[:data]
end
def create
current_user.update(setup_for_company: true) if params[:setup_for_company]
group_name = params[:setup_for_company] ? customer_params[:company] : "#{current_user.name}'s Group"
group = Groups::CreateService.new(current_user, name: group_name, path: SecureRandom.uuid).execute
return render json: group.errors.to_json unless group.persisted?
response = Subscriptions::CreateService.new(
current_user,
group: group,
customer_params: customer_params,
subscription_params: subscription_params
).execute
response[:data] = { location: edit_group_path(group) } if response[:success]
render json: response[:data]
end
private
def customer_params
params.require(:customer).permit(:country, :address_1, :address_2, :city, :state, :zip_code, :company)
end
def subscription_params
params.require(:subscription).permit(:plan_id, :payment_method_id, :quantity)
end
def client
Gitlab::SubscriptionPortal::Client
end
......
# frozen_string_literal: true
module Subscriptions
class CreateService
attr_reader :current_user, :customer_params, :subscription_params
def initialize(current_user, group:, customer_params:, subscription_params:)
@current_user = current_user
@group = group
@customer_params = customer_params
@subscription_params = subscription_params
end
def execute
response = client.create_customer(create_customer_params)
return response unless response[:success]
token = response.with_indifferent_access[:data][:customer][:authentication_token]
client.create_subscription(create_subscription_params, current_user.email, token)
end
private
def create_customer_params
{
provider: 'gitlab',
uid: current_user.id,
credentials: credentials_attrs,
customer: customer_attrs,
info: info_attrs
}
end
# Return an empty hash for now, because the Customers API requires the credentials attribute to be present,
# although it does not require the actual values. Remove this once the Customers API has been updated.
def credentials_attrs
{}
end
def customer_attrs
{
country: country_code(customer_params[:country]),
address_1: customer_params[:address_1],
address_2: customer_params[:address_2],
city: customer_params[:city],
state: customer_params[:state],
zip_code: customer_params[:zip_code],
company: customer_params[:company]
}
end
def info_attrs
{
first_name: current_user.first_name,
last_name: current_user.last_name,
email: current_user.email
}
end
def create_subscription_params
{
plan_id: subscription_params[:plan_id],
payment_method_id: subscription_params[:payment_method_id],
products: {
main: {
quantity: subscription_params[:quantity]
}
},
gl_namespace_id: @group.id,
gl_namespace_name: @group.name,
preview: 'false'
}
end
def country_code(country)
World.alpha3_from_alpha2(country)
end
def client
Gitlab::SubscriptionPortal::Client
end
end
end
# frozen_string_literal: true
resource :subscriptions, only: [:new] do
resource :subscriptions, only: [:new, :create] do
get :payment_form
get :payment_method
end
......@@ -25,4 +25,8 @@ module World
def all_countries
strong_memoize(:all_countries) { ISO3166::Country.all.reject {|item| DENYLIST.include?(item.name) } }
end
def alpha3_from_alpha2(alpha2)
ISO3166::Country[alpha2]&.alpha3
end
end
......@@ -15,7 +15,7 @@ describe SubscriptionsController do
end
context 'with unauthorized user' do
it { is_expected.to have_gitlab_http_status 302 }
it { is_expected.to have_gitlab_http_status(:redirect) }
it { is_expected.to redirect_to new_user_registration_path }
it 'stores subscription URL for later' do
......@@ -49,7 +49,7 @@ describe SubscriptionsController do
subject { get :payment_form, params: { id: 'cc' } }
context 'with unauthorized user' do
it { is_expected.to have_gitlab_http_status 302 }
it { is_expected.to have_gitlab_http_status(:redirect) }
it { is_expected.to redirect_to new_user_session_path }
end
......@@ -60,7 +60,7 @@ describe SubscriptionsController do
allow(Gitlab::SubscriptionPortal::Client).to receive(:payment_form_params).with('cc').and_return(client_response)
end
it { is_expected.to have_gitlab_http_status 200 }
it { is_expected.to have_gitlab_http_status(:ok) }
it 'returns the data attribute of the client response in JSON format' do
subject
......@@ -73,7 +73,7 @@ describe SubscriptionsController do
subject { get :payment_method, params: { id: 'xx' } }
context 'with unauthorized user' do
it { is_expected.to have_gitlab_http_status 302 }
it { is_expected.to have_gitlab_http_status(:redirect) }
it { is_expected.to redirect_to new_user_session_path }
end
......@@ -84,7 +84,7 @@ describe SubscriptionsController do
allow(Gitlab::SubscriptionPortal::Client).to receive(:payment_method).with('xx').and_return(client_response)
end
it { is_expected.to have_gitlab_http_status 200 }
it { is_expected.to have_gitlab_http_status(:ok) }
it 'returns the data attribute of the client response in JSON format' do
subject
......@@ -92,4 +92,86 @@ describe SubscriptionsController do
end
end
end
describe 'POST #create' do
subject do
post :create,
params: {
setup_for_company: setup_for_company,
customer: { country: 'NL' },
subscription: { plan_id: 'x' }
},
as: :json
end
let(:setup_for_company) { true }
context 'with unauthorized user' do
it { is_expected.to have_gitlab_http_status(:unauthorized) }
end
context 'with authorized user' do
let_it_be(:service_response) { { success: true, data: 'foo' } }
let_it_be(:group) { create(:group) }
before do
sign_in(user)
allow_any_instance_of(Subscriptions::CreateService).to receive(:execute).and_return(service_response)
allow_any_instance_of(EE::Groups::CreateService).to receive(:execute).and_return(group)
end
context 'when setting up for a company' do
it 'updates the setup_for_company attribute of the current user' do
expect { subject }.to change { user.reload.setup_for_company }.from(nil).to(true)
end
end
context 'when not setting up for a company' do
let(:setup_for_company) { false }
it 'does not update the setup_for_company attribute of the current user' do
expect { subject }.not_to change { user.reload.setup_for_company }
end
end
it 'creates a group' do
expect_any_instance_of(EE::Groups::CreateService).to receive(:execute)
subject
end
context 'when an error occurs creating a group' do
let(:group) { Group.new(path: 'foo') }
it 'returns the errors in json format' do
group.save
subject
expect(response.body).to eq({ name: ["can't be blank"] }.to_json)
end
end
context 'on successful creation of a subscription' do
it { is_expected.to have_gitlab_http_status(:ok) }
it 'returns the group edit location in JSON format' do
subject
expect(response.body).to eq({ location: "/groups/#{group.path}/-/edit" }.to_json)
end
end
context 'on unsuccessful creation of a subscription' do
let(:service_response) { { success: false, data: { errors: 'error message' } } }
it { is_expected.to have_gitlab_http_status(:ok) }
it 'returns the error message in JSON format' do
subject
expect(response.body).to eq('{"errors":"error message"}')
end
end
end
end
end
{
"customer": {
"provider": "gitlab",
"uid": 111,
"credentials": {},
"customer": {
"country": "NLD",
"address_1": "Address line 1",
"address_2": "Address line 2",
"city": "City",
"state": "State",
"zip_code": "Zip code",
"company": "My organization"
},
"info": {
"first_name": "First name",
"last_name": "Last name",
"email": "first.last@gitlab.com"
}
},
"subscription": {
"plan_id": "Plan ID",
"payment_method_id": "Payment method ID",
"products": {
"main": {
"quantity": 123
}
},
"gl_namespace_id": 222,
"gl_namespace_name": "Group name",
"preview": "false"
}
}
......@@ -32,4 +32,18 @@ describe World do
expect(result).to be_nil
end
end
describe '.alpha3_from_alpha2' do
it 'returns the three letter abbreviated country name' do
result = described_class.alpha3_from_alpha2('NL')
expect(result).to eq('NLD')
end
it 'returns nil when given country cannot be found' do
result = described_class.alpha3_from_alpha2('NLX')
expect(result).to be_nil
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Subscriptions::CreateService do
subject { described_class.new(user, group: group, customer_params: customer_params, subscription_params: subscription_params) }
let_it_be(:user) { create(:user, id: 111, first_name: 'First name', last_name: 'Last name', email: 'first.last@gitlab.com') }
let_it_be(:group) { create(:group, id: 222, name: 'Group name') }
let_it_be(:customer_params) do
{
country: 'NL',
address_1: 'Address line 1',
address_2: 'Address line 2',
city: 'City',
state: 'State',
zip_code: 'Zip code',
company: 'My organization'
}
end
let_it_be(:subscription_params) do
{
plan_id: 'Plan ID',
payment_method_id: 'Payment method ID',
quantity: 123
}
end
let_it_be(:client) { Gitlab::SubscriptionPortal::Client }
let_it_be(:create_service_params) { JSON.parse(fixture_file('create_service_params.json', dir: 'ee')).deep_symbolize_keys }
describe '#execute' do
context 'when failing to create a customer' do
before do
allow(client).to receive(:create_customer).and_return(success: false, data: { errors: 'failed to create customer' })
end
it 'returns the response hash' do
expect(subject.execute).to eq(success: false, data: { errors: 'failed to create customer' })
end
end
context 'when successfully creating a customer' do
before do
allow(client).to receive(:create_customer).and_return(success: true, data: { success: true, 'customer' => { 'authentication_token' => 'token' } })
end
it 'creates a subscription with the returned authentication token' do
expect(client).to receive(:create_subscription).with(anything, user.email, 'token')
subject.execute
end
context 'when failing to create a subscription' do
before do
allow(client).to receive(:create_subscription).and_return(success: false, data: { errors: 'failed to create subscription' })
end
it 'returns the response hash' do
expect(subject.execute).to eq(success: false, data: { errors: 'failed to create subscription' })
end
end
context 'when successfully creating a subscription' do
before do
allow(client).to receive(:create_subscription).and_return(success: true, data: { success: true, subscription_id: 'xxx' })
end
it 'returns the response hash' do
expect(subject.execute).to eq(success: true, data: { success: true, subscription_id: 'xxx' })
end
end
end
context 'passing the correct parameters to the client' do
before do
allow(client).to receive(:create_customer).and_return(success: true, data: { success: true, customer: { authentication_token: 'token' } })
allow(client).to receive(:create_subscription).and_return(success: true, data: { success: true, subscription_id: 'xxx' })
end
it 'passes the correct parameters for creating a customer' do
expect(client).to receive(:create_customer).with(create_service_params[:customer])
subject.execute
end
it 'passes the correct parameters for creating a subscription' do
expect(client).to receive(:create_subscription).with(create_service_params[:subscription], 'first.last@gitlab.com', 'token')
subject.execute
end
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment