1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-02-19 13:54:25 +01:00

handle reservations+subscription w/ schedule

integration testing for payment schedule
debug reservation process
This commit is contained in:
Sylvain 2020-12-14 17:42:23 +01:00
parent c3c881d2a8
commit 908ccf5bab
12 changed files with 122 additions and 46 deletions

View File

@ -103,7 +103,10 @@ class API::PaymentsController < API::ApiController
current_user.id
end
is_reserve = Reservations::Reserve.new(user_id, current_user.invoicing_profile.id)
.pay_and_save(@reservation, payment_details: details, payment_intent_id: intent.id)
.pay_and_save(@reservation,
payment_details: details,
payment_intent_id: intent.id,
schedule: params[:cart_items][:reservation][:payment_schedule])
if intent.class == Stripe::PaymentIntent
Stripe::PaymentIntent.update(
intent.id,

View File

@ -35,7 +35,9 @@ class API::ReservationsController < API::ApiController
@reservation = Reservation.new(reservation_params)
is_reserve = Reservations::Reserve.new(user_id, current_user.invoicing_profile.id)
.pay_and_save(@reservation, payment_details: price[:price_details])
.pay_and_save(@reservation,
payment_details: price[:price_details],
schedule: params[:reservation][:payment_schedule])
if is_reserve
SubscriptionExtensionAfterReservation.new(@reservation).extend_subscription_if_eligible

View File

@ -120,7 +120,7 @@ class Reservation < ApplicationRecord
pending_invoice_items.each(&:delete)
end
def save_with_payment(operator_profile_id, payment_details, payment_intent_id = nil)
def save_with_payment(operator_profile_id, payment_details, payment_intent_id = nil, schedule: false)
operator = InvoicingProfile.find(operator_profile_id)&.user
method = operator&.admin? || (operator&.manager? && operator != user) ? nil : 'stripe'
@ -138,7 +138,7 @@ class Reservation < ApplicationRecord
if plan_id
self.subscription = Subscription.find_or_initialize_by(statistic_profile_id: statistic_profile_id)
subscription.attributes = { plan_id: plan_id, statistic_profile_id: statistic_profile_id, expiration_date: nil }
if subscription.save_with_payment(operator_profile_id, invoice: false)
if subscription.save_with_payment(operator_profile_id, invoice: false, schedule: schedule)
invoice.invoice_items.push InvoiceItem.new(
amount: payment_details[:elements][:plan],
description: subscription.plan.name,

View File

@ -10,8 +10,7 @@ class Reservations::Reserve
end
def pay_and_save(reservation, payment_details: nil, payment_intent_id: nil, schedule: false)
# TODO, pass the schedule payment up to subscription.save_with_payment(... schedule: schedule)
reservation.statistic_profile_id = StatisticProfile.find_by(user_id: user_id).id
reservation.save_with_payment(operator_profile_id, payment_details, payment_intent_id)
reservation.save_with_payment(operator_profile_id, payment_details, payment_intent_id, schedule: schedule)
end
end

View File

@ -53,7 +53,6 @@ class StripeWorker
{ name: object.name },
{ api_key: Setting.get('stripe_secret_key') }
)
p.product
else
product = Stripe::Product.create(
{

View File

@ -108,8 +108,8 @@ SET default_tablespace = '';
CREATE TABLE public.abuses (
id integer NOT NULL,
signaled_id integer,
signaled_type character varying,
signaled_id integer,
first_name character varying,
last_name character varying,
email character varying,
@ -187,8 +187,8 @@ CREATE TABLE public.addresses (
locality character varying,
country character varying,
postal_code character varying,
placeable_id integer,
placeable_type character varying,
placeable_id integer,
created_at timestamp without time zone,
updated_at timestamp without time zone
);
@ -263,8 +263,8 @@ CREATE TABLE public.ar_internal_metadata (
CREATE TABLE public.assets (
id integer NOT NULL,
viewable_id integer,
viewable_type character varying,
viewable_id integer,
attachment character varying,
type character varying,
created_at timestamp without time zone,
@ -504,8 +504,8 @@ ALTER SEQUENCE public.coupons_id_seq OWNED BY public.coupons.id;
CREATE TABLE public.credits (
id integer NOT NULL,
creditable_id integer,
creditable_type character varying,
creditable_id integer,
plan_id integer,
hours integer,
created_at timestamp without time zone,
@ -1046,8 +1046,8 @@ ALTER SEQUENCE public.invoice_items_id_seq OWNED BY public.invoice_items.id;
CREATE TABLE public.invoices (
id integer NOT NULL,
invoiced_id integer,
invoiced_type character varying,
invoiced_id integer,
stp_invoice_id character varying,
total integer,
created_at timestamp without time zone,
@ -1227,15 +1227,15 @@ ALTER SEQUENCE public.machines_id_seq OWNED BY public.machines.id;
CREATE TABLE public.notifications (
id integer NOT NULL,
receiver_id integer,
attached_object_id integer,
attached_object_type character varying,
attached_object_id integer,
notification_type_id integer,
is_read boolean DEFAULT false,
created_at timestamp without time zone,
updated_at timestamp without time zone,
receiver_type character varying,
is_send boolean DEFAULT false,
meta_data jsonb DEFAULT '{}'::jsonb
meta_data jsonb DEFAULT '"{}"'::jsonb
);
@ -1656,8 +1656,8 @@ CREATE TABLE public.prices (
id integer NOT NULL,
group_id integer,
plan_id integer,
priceable_id integer,
priceable_type character varying,
priceable_id integer,
amount integer,
created_at timestamp without time zone NOT NULL,
updated_at timestamp without time zone NOT NULL
@ -1972,8 +1972,8 @@ CREATE TABLE public.reservations (
message text,
created_at timestamp without time zone,
updated_at timestamp without time zone,
reservable_id integer,
reservable_type character varying,
reservable_id integer,
nb_reserve_places integer,
statistic_profile_id integer
);
@ -2005,8 +2005,8 @@ ALTER SEQUENCE public.reservations_id_seq OWNED BY public.reservations.id;
CREATE TABLE public.roles (
id integer NOT NULL,
name character varying,
resource_id integer,
resource_type character varying,
resource_id integer,
created_at timestamp without time zone,
updated_at timestamp without time zone
);
@ -2942,8 +2942,8 @@ CREATE TABLE public.users_roles (
CREATE TABLE public.wallet_transactions (
id integer NOT NULL,
wallet_id integer,
transactable_id integer,
transactable_type character varying,
transactable_id integer,
transaction_type character varying,
amount integer,
created_at timestamp without time zone NOT NULL,
@ -4032,6 +4032,14 @@ ALTER TABLE ONLY public.roles
ADD CONSTRAINT roles_pkey PRIMARY KEY (id);
--
-- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.schema_migrations
ADD CONSTRAINT schema_migrations_pkey PRIMARY KEY (version);
--
-- Name: settings settings_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
@ -5096,29 +5104,6 @@ CREATE INDEX profiles_lower_unaccent_last_name_trgm_idx ON public.profiles USING
CREATE INDEX projects_search_vector_idx ON public.projects USING gin (search_vector);
--
-- Name: unique_schema_migrations; Type: INDEX; Schema: public; Owner: -
--
CREATE UNIQUE INDEX unique_schema_migrations ON public.schema_migrations USING btree (version);
--
-- Name: accounting_periods accounting_periods_del_protect; Type: RULE; Schema: public; Owner: -
--
CREATE RULE accounting_periods_del_protect AS
ON DELETE TO public.accounting_periods DO INSTEAD NOTHING;
--
-- Name: accounting_periods accounting_periods_upd_protect; Type: RULE; Schema: public; Owner: -
--
CREATE RULE accounting_periods_upd_protect AS
ON UPDATE TO public.accounting_periods DO INSTEAD NOTHING;
--
-- Name: projects projects_search_content_trigger; Type: TRIGGER; Schema: public; Owner: -
--
@ -5653,7 +5638,6 @@ INSERT INTO "schema_migrations" (version) VALUES
('20140605125131'),
('20140605142133'),
('20140605151442'),
('20140606133116'),
('20140609092700'),
('20140609092827'),
('20140610153123'),
@ -5722,14 +5706,12 @@ INSERT INTO "schema_migrations" (version) VALUES
('20150507075620'),
('20150512123546'),
('20150520132030'),
('20150520133409'),
('20150526130729'),
('20150527153312'),
('20150529113555'),
('20150601125944'),
('20150603104502'),
('20150603104658'),
('20150603133050'),
('20150604081757'),
('20150604131525'),
('20150608142234'),
@ -5811,7 +5793,6 @@ INSERT INTO "schema_migrations" (version) VALUES
('20160905142700'),
('20160906094739'),
('20160906094847'),
('20160906145713'),
('20160915105234'),
('20161123104604'),
('20170109085345'),

View File

@ -100,7 +100,7 @@ value_history_10:
id: 10
setting_id: 10
invoicing_profile_id: 1
value: YYMMmmmX[/VL]R[/A]S[/E]
value: YYMMmmmX[/VL]R[/A]
created_at: 2018-12-17 11:23:01.603733000 Z
updated_at: 2018-12-17 11:23:01.603733000 Z
footprint: ed23a2eb1903befc977621bc3c3b19aad831fe550ebaa99e9299238b3d93c275
@ -693,3 +693,11 @@ history_value_73:
value: true
created_at: 2020-06-17 10:48:19.002417000 Z
updated_at: 2020-06-17 10:48:19.002417000 Z
value_history_74:
id: 74
setting_id: 10
invoicing_profile_id: 1
value: YYMMmmmX[/VL]R[/A]S[/E]
created_at: 2020-12-14 14:37:35.615124000 Z
updated_at: 2020-12-14 14:37:35.615124000 Z

View File

@ -22,6 +22,7 @@ machine_1:
created_at: 2016-04-04 14:11:34.210242000 Z
updated_at: 2016-04-04 14:11:34.210242000 Z
slug: decoupeuse-laser
stp_product_id: prod_IZPyHpMCl38iQl
machine_2:
id: 2
@ -38,6 +39,7 @@ machine_2:
created_at: 2016-04-04 14:11:34.274025000 Z
updated_at: 2016-04-04 14:11:34.274025000 Z
slug: decoupeuse-vinyle
stp_product_id: prod_IZPyPShaaRgSML
machine_3:
id: 3
@ -54,6 +56,7 @@ machine_3:
created_at: 2016-04-04 14:11:34.304247000 Z
updated_at: 2016-04-04 14:11:34.304247000 Z
slug: shopbot-grande-fraiseuse
stp_product_id: prod_IZPyEjmdfMowhY
machine_4:
id: 4
@ -67,6 +70,7 @@ machine_4:
created_at: 2001-01-01 14:11:34.341810000 Z
updated_at: 2001-01-01 14:11:34.341810000 Z
slug: imprimante-3d
stp_product_id: prod_IZPy85vZOQpAo5
machine_5:
id: 5
@ -89,6 +93,7 @@ machine_5:
created_at: 2016-04-04 14:11:34.379481000 Z
updated_at: 2016-04-04 14:11:34.379481000 Z
slug: petite-fraiseuse
stp_product_id: prod_IZPyBJEgbcpWMC
machine_6:
id: 6
@ -123,3 +128,4 @@ machine_6:
created_at: 2016-04-04 14:11:34.424740000 Z
updated_at: 2016-04-04 14:11:34.424740000 Z
slug: form1-imprimante-3d
stp_product_id: prod_IZPyjCzvLmLWAz

View File

@ -15,6 +15,8 @@ plan_1:
base_name: Mensuel
ui_weight: 1
interval_count: 1
slug: mensuel
stp_product_id: prod_IZPyXhfyNiGkWR
plan_2:
id: 2
@ -32,6 +34,8 @@ plan_2:
base_name: Sleede
ui_weight: 5
interval_count: 2
slug: sleede
stp_product_id: prod_IZPykam7a4satn
plan_3:
id: 3
@ -49,4 +53,26 @@ plan_3:
type: Plan
base_name: Mensuel tarif réduit
ui_weight: 0
interval_count: 1*
slug: mensuel-tarif-reduit
stp_product_id: prod_IZPyM4N36h86G0
plan_schedulable:
id: 4
name: Abonnement mensualisable - standard, association, year
amount: 113600
interval: year
group_id: 1
stp_plan_id:
created_at: 2020-12-14 14:10:11.056241000 Z
updated_at: 2020-12-14 14:10:11.137421000 Z
training_credit_nb: 1
is_rolling: true
description:
type: Plan
base_name: Abonnement mensualisable
ui_weight: 10
interval_count: 1
monthly_payment: true
slug: abonnement-mensualisable
stp_product_id: prod_IZQAhb9nLu4jfN

View File

@ -7,3 +7,4 @@ space_1:
created_at: 2017-02-15 15:55:04.123928000 Z
updated_at: 2017-02-15 15:55:04.123928000 Z
characteristics: Scie à chantourner, rabot, dégauchisseuse, chanfreineuse et pyrograveur
stp_product_id: prod_IZPyHjIb2owoB8

View File

@ -7,6 +7,7 @@ training_1:
nb_total_places:
slug: formation-imprimante-3d
description:
stp_product_id: prod_IZPyXw6BDBBFOg
training_2:
id: 2
@ -16,6 +17,7 @@ training_2:
nb_total_places:
slug: formation-laser-vinyle
description:
stp_product_id: prod_IZPytTl1wSB5jH
training_3:
id: 3
@ -25,6 +27,7 @@ training_3:
nb_total_places:
slug: formation-petite-fraiseuse-numerique
description:
stp_product_id: prod_IZPyAA1A4QfEyL
training_4:
id: 4
@ -34,6 +37,7 @@ training_4:
nb_total_places:
slug: formation-shopbot-grande-fraiseuse
description:
stp_product_id: prod_IZPyU27NjDSmqB
training_5:
id: 5
@ -43,3 +47,4 @@ training_5:
nb_total_places:
slug: formation-logiciel-2d
description:
stp_product_id: prod_IZPyvdgQHMByB3

View File

@ -1,5 +1,7 @@
# frozen_string_literal: true
require 'test_helper'
class Subscriptions::CreateAsUserTest < ActionDispatch::IntegrationTest
setup do
@user = User.find_by(username: 'jdupond')
@ -166,4 +168,48 @@ class Subscriptions::CreateAsUserTest < ActionDispatch::IntegrationTest
assert_equal invoice.wallet_amount / 100.0, transaction.amount
assert_equal invoice.wallet_transaction_id, transaction.id
end
test 'user takes a subscription with payment schedule' do
plan = Plan.find_by(group_id: @user.group.id, type: 'Plan', base_name: 'Abonnement mensualisable')
VCR.use_cassette('subscriptions_user_setup_intent') do
get "/api/payments/setup_intent/#{@user.id}"
end
# Check response format & status
assert_equal 200, response.status, response.body
assert_equal Mime[:json], response.content_type
# Check the correct object was signaled
setup_intent = json_response(response.body)
VCR.use_cassette('subscriptions_user_create_with_payment_schedule') do
post '/api/payments/confirm_payment',
params: {
payment_method_id: stripe_payment_method,
cart_items: {
subscription: {
plan_id: plan.id,
payment_schedule: true,
payment_method: 'stripe'
}
},
setup_intent_id: setup_intent[:client_secret]
}.to_json, headers: default_headers
end
# Check response format & status
assert_equal 201, response.status, response.body
assert_equal Mime[:json], response.content_type
# Check the correct plan was subscribed
subscription = json_response(response.body)
assert_equal plan.id, subscription[:plan_id], 'subscribed plan does not match'
# Check that the user has the correct subscription
assert_not_nil @user.subscription, "user's subscription was not found"
assert_not_nil @user.subscription.plan, "user's subscribed plan was not found"
assert_equal plan.id, @user.subscription.plan_id, "user's plan does not match"
end
end