1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2024-11-28 09:24:24 +01:00

(feat) limit machine visibility per plan

This commit is contained in:
Sylvain 2023-03-15 14:17:06 +01:00
parent 7ad1f8da3f
commit 6089e636e4
25 changed files with 594 additions and 513 deletions

View File

@ -82,6 +82,7 @@ class API::PlansController < API::ApiController
@parameters = @parameters.require(:plan)
.permit(:base_name, :type, :group_id, :amount, :interval, :interval_count, :is_rolling, :limiting,
:training_credit_nb, :ui_weight, :disabled, :monthly_payment, :description, :plan_category_id,
:machines_visibility,
plan_file_attributes: %i[id attachment _destroy],
prices_attributes: %i[id amount],
advanced_accounting_attributes: %i[code analytical_section],

View File

@ -9,7 +9,7 @@ import { FieldArrayPath } from 'react-hook-form/dist/types/path';
interface FormUnsavedListProps<TFieldValues, TFieldArrayName extends FieldArrayPath<TFieldValues>, TKeyName extends string> {
fields: Array<FieldArrayWithId<TFieldValues, TFieldArrayName, TKeyName>>,
onRemove?: (index: number) => void,
onRemove: (index: number) => void,
register: UseFormRegister<TFieldValues>,
className?: string,
title: string,
@ -24,6 +24,19 @@ interface FormUnsavedListProps<TFieldValues, TFieldArrayName extends FieldArrayP
/**
* This component render a list of unsaved attributes, created elsewhere than in the form (e.g. in a modal dialog)
* and pending for the form to be saved.
*
* The `renderField` attribute should return a JSX element composed like the following example:
* ```
* <> <!-- empty tag -->
* <div className="group"> <!-- the group class is important -->
* <span>Attribute 1</span> <!-- a span tag for the title -->
* <p>{item.attr1}</p> <!-- a paragraph tag for the value -->
* </div>
* <div className="group">
* ...
* </div>
* </>
* ```
*/
export const FormUnsavedList = <TFieldValues extends FieldValues = FieldValues, TFieldArrayName extends FieldArrayPath<TFieldValues> = FieldArrayPath<TFieldValues>, TKeyName extends string = 'id'>({ fields, onRemove, register, className, title, shouldRenderField = () => true, renderField, formAttributeName, formAttributes, saveReminderLabel, cancelLabel }: FormUnsavedListProps<TFieldValues, TFieldArrayName, TKeyName>) => {
const { t } = useTranslation('shared');

View File

@ -56,13 +56,19 @@ export const PlanForm: React.FC<PlanFormProps> = ({ action, plan, onError, onSuc
useEffect(() => {
GroupAPI.index({ disabled: false })
.then(res => setGroups(res.map(g => { return { value: g.id, label: g.name }; })))
.then(res => setGroups(res.map(g => {
return { value: g.id, label: g.name };
})))
.catch(onError);
PlanCategoryAPI.index()
.then(res => setCategories(res.map(c => { return { value: c.id, label: c.name }; })))
.then(res => setCategories(res.map(c => {
return { value: c.id, label: c.name };
})))
.catch(onError);
UserAPI.index({ role: 'partner' })
.then(res => setPartners(res.map(p => { return { value: p.id, label: p.name }; })))
.then(res => setPartners(res.map(p => {
return { value: p.id, label: p.name };
})))
.catch(onError);
}, []);
@ -106,7 +112,9 @@ export const PlanForm: React.FC<PlanFormProps> = ({ action, plan, onError, onSuc
* Return the available options for the plan period
*/
const buildPeriodsOptions = (): Array<SelectOption<string>> => {
return ['week', 'month', 'year'].map(d => { return { value: d, label: t(`app.admin.plan_form.${d}`) }; });
return ['week', 'month', 'year'].map(d => {
return { value: d, label: t(`app.admin.plan_form.${d}`) };
});
};
/**
@ -139,7 +147,7 @@ export const PlanForm: React.FC<PlanFormProps> = ({ action, plan, onError, onSuc
* Render the content of the 'subscriptions settings' tab
*/
const renderSettingsTab = () => (
<div className='plan-form-content'>
<div className="plan-form-content">
<section>
<header>
<p className="title">{t('app.admin.plan_form.description')}</p>
@ -183,12 +191,12 @@ export const PlanForm: React.FC<PlanFormProps> = ({ action, plan, onError, onSuc
tooltip={t('app.admin.plan_form.transversal_help')}
id="all_groups" />}
{!allGroups && groups && <FormSelect options={groups}
formState={formState}
control={control}
rules={{ required: !allGroups }}
disabled={action === 'update'}
label={t('app.admin.plan_form.group')}
id="group_id" />}
formState={formState}
control={control}
rules={{ required: !allGroups }}
disabled={action === 'update'}
label={t('app.admin.plan_form.group')}
id="group_id" />}
<div className="grp">
<FormInput register={register}
rules={{ required: true, min: 1 }}
@ -262,12 +270,12 @@ export const PlanForm: React.FC<PlanFormProps> = ({ action, plan, onError, onSuc
<FormInput register={register} type="hidden" id="type" defaultValue="Plan" />
{output.type === 'PartnerPlan' && <div className="partner">
{partners && <FormSelect id="partner_id"
options={partners}
control={control}
formState={formState}
rules={{ required: output.type === 'PartnerPlan' }}
tooltip={t('app.admin.plan_form.alert_partner_notification')}
label={t('app.admin.plan_form.notified_partner')} />}
options={partners}
control={control}
formState={formState}
rules={{ required: output.type === 'PartnerPlan' }}
tooltip={t('app.admin.plan_form.alert_partner_notification')}
label={t('app.admin.plan_form.notified_partner')} />}
<FabButton className="is-secondary" icon={<UserPlus size={20} />} onClick={tooglePartnerModal}>
{t('app.admin.plan_form.new_user')}
</FabButton>
@ -275,6 +283,21 @@ export const PlanForm: React.FC<PlanFormProps> = ({ action, plan, onError, onSuc
</div>
</section>
<section>
<header>
<p className="title">{t('app.admin.plan_form.slots_visibility')}</p>
<p className="description">{t('app.admin.plan_form.slots_visibility_help')}</p>
</header>
<div className="content">
<FormInput register={register}
formState={formState}
nullable
id="machines_visibility"
type="number"
label={t('app.admin.plan_form.machines_visibility')} />
</div>
</section>
<section>
<header>
<p className="title">{t('app.admin.plan_form.display')} </p>
@ -287,11 +310,11 @@ export const PlanForm: React.FC<PlanFormProps> = ({ action, plan, onError, onSuc
tooltip={t('app.admin.plan_form.category_help')}
label={t('app.admin.plan_form.category')} />}
<FormInput register={register}
formState={formState}
id="ui_weight"
type="number"
label={t('app.admin.plan_form.visual_prominence')}
tooltip={t('app.admin.plan_form.visual_prominence_help')} />
formState={formState}
id="ui_weight"
type="number"
label={t('app.admin.plan_form.visual_prominence')}
tooltip={t('app.admin.plan_form.visual_prominence_help')} />
</div>
</section>
@ -300,10 +323,10 @@ export const PlanForm: React.FC<PlanFormProps> = ({ action, plan, onError, onSuc
</section>
{action === 'update' && <PlanPricingForm formState={formState}
control={control}
onError={onError}
setValue={setValue}
register={register} />}
control={control}
onError={onError}
setValue={setValue}
register={register} />}
</div>
);

View File

@ -60,11 +60,11 @@
}
&.is-destroying {
background-color: var(--main-lightest);
background-color: var(--alert-lightest);
.marker {
text-align: center;
font-weight: 500;
color: var(--main-dark);
color: var(--alert-dark);
margin: auto;
}
.actions > .cancel-action {

View File

@ -64,7 +64,6 @@ class CartItem::Reservation < CartItem::BaseItem
plan = pending_subscription&.plan || customer&.subscribed_plan
reservation_deadline_minutes = Setting.get('reservation_deadline').to_i
reservation_deadline = reservation_deadline_minutes.minutes.since
unless ReservationLimitService.authorized?(plan, customer, self, all_items)
errors.add(:reservation, I18n.t('cart_item_validation.limit_reached', {
@ -75,7 +74,7 @@ class CartItem::Reservation < CartItem::BaseItem
end
cart_item_reservation_slots.each do |sr|
return false unless validate_slot_reservation(sr, pending_subscription, reservation_deadline, errors)
return false unless validate_slot_reservation(sr, pending_subscription, reservation_deadline_minutes, errors)
end
true
@ -249,7 +248,7 @@ class CartItem::Reservation < CartItem::BaseItem
# @param reservation_slot [CartItem::ReservationSlot]
# @param pending_subscription [CartItem::Subscription, NilClass]
# @param reservation_deadline [Date,Time]
# @param reservation_deadline [Integer]
# @param errors [ActiveModel::Errors]
# @return [Boolean]
def validate_slot_reservation(reservation_slot, pending_subscription, reservation_deadline, errors)
@ -270,12 +269,12 @@ class CartItem::Reservation < CartItem::BaseItem
return false
end
if slot.start_at < reservation_deadline && !operator.privileged?
errors.add(:slot, I18n.t('cart_item_validation.deadline', { MINUTES: reservation_deadline_minutes }))
if slot.start_at < reservation_deadline.minutes.since && !operator.privileged?
errors.add(:slot, I18n.t('cart_item_validation.deadline', { MINUTES: reservation_deadline }))
return false
end
unless availability.plan_ids.empty? && required_subscription?(availability, pending_subscription)
if availability.plan_ids.any? && !required_subscription?(availability, pending_subscription)
errors.add(:availability, I18n.t('cart_item_validation.restricted'))
return false
end

View File

@ -125,27 +125,17 @@ class Availabilities::AvailabilitiesService
# @param range_end [ActiveSupport::TimeWithZone]
# @return ActiveRecord::Relation<Availability>
def availabilities(availabilities, type, user, range_start, range_end)
# who made the request?
# 1) an admin (he can see all availabilities from 1 month ago to anytime in the future)
if @current_user&.admin? || @current_user&.manager?
window_start = [range_start, 1.month.ago].max
availabilities.includes(:tags, :slots)
.joins(:slots)
.where('availabilities.start_at <= ? AND availabilities.end_at >= ? AND available_type = ?', range_end, window_start, type)
.where('slots.start_at > ? AND slots.end_at < ?', window_start, range_end)
# 2) an user (he cannot see past availabilities neither those further than 1 (or 3) months in the future)
else
end_at = @maximum_visibility[:other]
end_at = @maximum_visibility[:year] if subscription_year?(user) && type != 'training'
end_at = @maximum_visibility[:year] if show_more_trainings?(user) && type == 'training'
window_end = [end_at, range_end].min
window_start = [range_start, @minimum_visibility].max
availabilities.includes(:tags, :slots)
.joins(:slots)
.where('availabilities.start_at <= ? AND availabilities.end_at >= ? AND available_type = ?', window_end, window_start, type)
.where('slots.start_at > ? AND slots.end_at < ?', window_start, window_end)
.where('availability_tags.tag_id' => user&.tag_ids&.concat([nil]))
.where(lock: false)
window = Availabilities::VisibilityService.new.visibility(@current_user, type, range_start, range_end)
qry = availabilities.includes(:tags, :slots)
.joins(:slots)
.where('availabilities.start_at <= ? AND availabilities.end_at >= ? AND available_type = ?', window[1], window[0], type)
.where('slots.start_at > ? AND slots.end_at < ?', window[0], window[1])
unless @current_user&.privileged?
# non priviledged users cannot see availabilities with tags different than their own and locked tags
qry = qry.where('availability_tags.tag_id' => user&.tag_ids&.concat([nil]))
.where(lock: false)
end
qry
end
end

View File

@ -0,0 +1,60 @@
# frozen_string_literal: true
# Return the maximum available visibility for a user
class Availabilities::VisibilityService
def initialize
@maximum_visibility = {
year: Setting.get('visibility_yearly').to_i.months.since,
other: Setting.get('visibility_others').to_i.months.since
}
@minimum_visibility = Setting.get('reservation_deadline').to_i.minutes.since
end
# @param user [User,NilClass]
# @param available_type [String] 'training', 'space', 'machine' or 'event'
# @param range_start [ActiveSupport::TimeWithZone]
# @param range_end [ActiveSupport::TimeWithZone]
# @return [Array<ActiveSupport::TimeWithZone,Date,Time>] as: [start,end]
def visibility(user, available_type, range_start, range_end)
if user&.privileged?
window_start = [range_start, 1.month.ago].max
window_end = range_end
else
end_at = @maximum_visibility[:other]
end_at = @maximum_visibility[:year] if subscription_year?(user) && available_type != 'training'
end_at = @maximum_visibility[:year] if show_more_trainings?(user) && available_type == 'training'
end_at = subscription_visibility(user, available_type) || end_at
window_end = [end_at, range_end].min
window_start = [range_start, @minimum_visibility].max
end
[window_start, window_end]
end
private
# @param user [User,NilClass]
def subscription_year?(user)
user&.subscribed_plan &&
(user&.subscribed_plan&.interval == 'year' ||
(user&.subscribed_plan&.interval == 'month' && user.subscribed_plan.interval_count >= 12))
end
# @param user [User,NilClass]
# @param available_type [String] 'training', 'space', 'machine' or 'event'
# @return [Time,NilClass]
def subscription_visibility(user, available_type)
return nil unless user&.subscribed_plan
return nil unless available_type == 'machine'
machines = user&.subscribed_plan&.machines_visibility
machines&.hours&.since
end
# members must have validated at least 1 training and must have a valid yearly subscription to view
# the trainings further in the futur. This is used to prevent users with a rolling subscription to take
# their first training in a very long delay.
# @param user [User,NilClass]
def show_more_trainings?(user)
user&.trainings&.size&.positive? && subscription_year?(user)
end
end

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
json.extract! plan, :id, :base_name, :name, :interval, :interval_count, :group_id, :training_credit_nb, :is_rolling, :description, :type,
:ui_weight, :disabled, :monthly_payment, :plan_category_id, :limiting
:ui_weight, :disabled, :monthly_payment, :plan_category_id, :limiting, :machines_visibility
json.amount plan.amount / 100.00
json.prices_attributes plan.prices, partial: 'api/prices/price', as: :price
if plan.plan_file

View File

@ -191,6 +191,9 @@ de:
partner_plan: "Partner plan"
partner_plan_help: "You can sell subscriptions in partnership with another organization. By doing so, the other organization will be notified when a member subscribes to this subscription plan."
partner_created: "The partner was successfully created"
slots_visibility: "Slots visibility"
slots_visibility_help: "You can determine how far in advance subscribers can view and reserve machine slots. When this setting is set, it takes precedence over the general settings."
machines_visibility: "Visibility time limit, in hours (machines)"
save: "Save"
create_success: "Plan(s) successfully created. Don't forget to redefine prices."
update_success: "The plan was updated successfully"

View File

@ -191,6 +191,9 @@ en:
partner_plan: "Partner plan"
partner_plan_help: "You can sell subscriptions in partnership with another organization. By doing so, the other organization will be notified when a member subscribes to this subscription plan."
partner_created: "The partner was successfully created"
slots_visibility: "Slots visibility"
slots_visibility_help: "You can determine how far in advance subscribers can view and reserve machine slots. When this setting is set, it takes precedence over the general settings."
machines_visibility: "Visibility time limit, in hours (machines)"
save: "Save"
create_success: "Plan(s) successfully created. Don't forget to redefine prices."
update_success: "The plan was updated successfully"

View File

@ -191,6 +191,9 @@ es:
partner_plan: "Partner plan"
partner_plan_help: "You can sell subscriptions in partnership with another organization. By doing so, the other organization will be notified when a member subscribes to this subscription plan."
partner_created: "The partner was successfully created"
slots_visibility: "Slots visibility"
slots_visibility_help: "You can determine how far in advance subscribers can view and reserve machine slots. When this setting is set, it takes precedence over the general settings."
machines_visibility: "Visibility time limit, in hours (machines)"
save: "Save"
create_success: "Plan(s) successfully created. Don't forget to redefine prices."
update_success: "The plan was updated successfully"

View File

@ -191,6 +191,9 @@ fr:
partner_plan: "Abonnement partenaire"
partner_plan_help: "Vous pouvez vendre des abonnements en partenariat avec un autre organisme. Ce faisant, l'autre entité sera informée lorsqu'un membre s'abonne à cette formule d'abonnement."
partner_created: "Le partenaire a bien été créé"
slots_visibility: "Visibilité des créneaux"
slots_visibility_help: "Vous pouvez déterminer combien de temps en avance les abonnés peuvent voir et réserver les créneaux machines. Lorsque ce paramètre est défini, il devient prioritaire sur les paramètres généraux."
machines_visibility: "Délai de visibilité, en heures (machines)"
save: "Enregistrer"
create_success: "Création du/des formule(s) d'abonnement réussie(s). N'oubliez pas de redéfinir les tarifs."
update_success: "La formule d'abonnement a bien été mise à jour"

View File

@ -191,6 +191,9 @@
partner_plan: "Partner plan"
partner_plan_help: "You can sell subscriptions in partnership with another organization. By doing so, the other organization will be notified when a member subscribes to this subscription plan."
partner_created: "The partner was successfully created"
slots_visibility: "Slots visibility"
slots_visibility_help: "You can determine how far in advance subscribers can view and reserve machine slots. When this setting is set, it takes precedence over the general settings."
machines_visibility: "Visibility time limit, in hours (machines)"
save: "Save"
create_success: "Plan(s) successfully created. Don't forget to redefine prices."
update_success: "The plan was updated successfully"

View File

@ -191,6 +191,9 @@ pt:
partner_plan: "Partner plan"
partner_plan_help: "You can sell subscriptions in partnership with another organization. By doing so, the other organization will be notified when a member subscribes to this subscription plan."
partner_created: "The partner was successfully created"
slots_visibility: "Slots visibility"
slots_visibility_help: "You can determine how far in advance subscribers can view and reserve machine slots. When this setting is set, it takes precedence over the general settings."
machines_visibility: "Visibility time limit, in hours (machines)"
save: "Save"
create_success: "Plan(s) successfully created. Don't forget to redefine prices."
update_success: "The plan was updated successfully"

View File

@ -191,6 +191,9 @@ zu:
partner_plan: "crwdns31949:0crwdne31949:0"
partner_plan_help: "crwdns31951:0crwdne31951:0"
partner_created: "crwdns31953:0crwdne31953:0"
slots_visibility: "crwdns37491:0crwdne37491:0"
slots_visibility_help: "crwdns37493:0crwdne37493:0"
machines_visibility: "crwdns37495:0crwdne37495:0"
save: "crwdns37411:0crwdne37411:0"
create_success: "crwdns31957:0crwdne31957:0"
update_success: "crwdns31959:0crwdne31959:0"

View File

@ -0,0 +1,9 @@
# frozen_string_literal:true
# From this migration, we add a machines_visibility parameter to plans.
# This parameter determines how far in advance subscribers can view and reserve machine slots.
class AddMachineVisibilityToPlan < ActiveRecord::Migration[5.2]
def change
add_column :plans, :machines_visibility, :integer
end
end

View File

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2023_03_09_094535) do
ActiveRecord::Schema.define(version: 2023_03_15_095054) do
# These are extensions that must be enabled in order to support this database
enable_extension "fuzzystrmatch"
@ -19,8 +19,8 @@ ActiveRecord::Schema.define(version: 2023_03_09_094535) do
enable_extension "unaccent"
create_table "abuses", id: :serial, force: :cascade do |t|
t.integer "signaled_id"
t.string "signaled_type"
t.integer "signaled_id"
t.string "first_name"
t.string "last_name"
t.string "email"
@ -68,8 +68,8 @@ ActiveRecord::Schema.define(version: 2023_03_09_094535) do
t.string "locality"
t.string "country"
t.string "postal_code"
t.integer "placeable_id"
t.string "placeable_type"
t.integer "placeable_id"
t.datetime "created_at"
t.datetime "updated_at"
end
@ -93,8 +93,8 @@ ActiveRecord::Schema.define(version: 2023_03_09_094535) do
end
create_table "assets", id: :serial, force: :cascade do |t|
t.integer "viewable_id"
t.string "viewable_type"
t.integer "viewable_id"
t.string "attachment"
t.string "type"
t.datetime "created_at"
@ -164,10 +164,10 @@ ActiveRecord::Schema.define(version: 2023_03_09_094535) do
create_table "cart_item_event_reservation_tickets", force: :cascade do |t|
t.integer "booked"
t.bigint "event_price_category_id"
t.bigint "cart_item_event_reservation_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "event_price_category_id"
t.index ["cart_item_event_reservation_id"], name: "index_cart_item_tickets_on_cart_item_event_reservation"
t.index ["event_price_category_id"], name: "index_cart_item_tickets_on_event_price_category"
end
@ -282,8 +282,8 @@ ActiveRecord::Schema.define(version: 2023_03_09_094535) do
end
create_table "credits", id: :serial, force: :cascade do |t|
t.integer "creditable_id"
t.string "creditable_type"
t.integer "creditable_id"
t.integer "plan_id"
t.integer "hours"
t.datetime "created_at"
@ -546,15 +546,15 @@ ActiveRecord::Schema.define(version: 2023_03_09_094535) do
create_table "notifications", id: :serial, force: :cascade do |t|
t.integer "receiver_id"
t.integer "attached_object_id"
t.string "attached_object_type"
t.integer "attached_object_id"
t.integer "notification_type_id"
t.boolean "is_read", default: false
t.datetime "created_at"
t.datetime "updated_at"
t.string "receiver_type"
t.boolean "is_send", default: false
t.jsonb "meta_data", default: {}
t.jsonb "meta_data", default: "{}"
t.index ["notification_type_id"], name: "index_notifications_on_notification_type_id"
t.index ["receiver_id"], name: "index_notifications_on_receiver_id"
end
@ -771,6 +771,7 @@ ActiveRecord::Schema.define(version: 2023_03_09_094535) do
t.boolean "monthly_payment"
t.bigint "plan_category_id"
t.boolean "limiting"
t.integer "machines_visibility"
t.index ["group_id"], name: "index_plans_on_group_id"
t.index ["plan_category_id"], name: "index_plans_on_plan_category_id"
end
@ -812,15 +813,14 @@ ActiveRecord::Schema.define(version: 2023_03_09_094535) do
t.text "conditions"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index "btrim(lower((name)::text))", name: "index_price_categories_on_TRIM_BOTH_FROM_LOWER_name", unique: true
t.index ["name"], name: "index_price_categories_on_name", unique: true
end
create_table "prices", id: :serial, force: :cascade do |t|
t.integer "group_id"
t.integer "plan_id"
t.integer "priceable_id"
t.string "priceable_type"
t.integer "priceable_id"
t.integer "amount"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
@ -984,8 +984,8 @@ ActiveRecord::Schema.define(version: 2023_03_09_094535) do
t.text "message"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "reservable_id"
t.string "reservable_type"
t.integer "reservable_id"
t.integer "nb_reserve_places"
t.integer "statistic_profile_id"
t.index ["reservable_type", "reservable_id"], name: "index_reservations_on_reservable_type_and_reservable_id"
@ -994,8 +994,8 @@ ActiveRecord::Schema.define(version: 2023_03_09_094535) do
create_table "roles", id: :serial, force: :cascade do |t|
t.string "name"
t.integer "resource_id"
t.string "resource_type"
t.integer "resource_id"
t.datetime "created_at"
t.datetime "updated_at"
t.index ["name", "resource_type", "resource_id"], name: "index_roles_on_name_and_resource_type_and_resource_id"

View File

@ -1,3 +1,4 @@
# admin without subscription
user_1:
id: 1
username: admin
@ -30,6 +31,7 @@ user_1:
merged_at:
is_allow_newsletter: true
# member without subscription
user_2:
id: 2
username: jdupond
@ -62,6 +64,7 @@ user_2:
merged_at:
is_allow_newsletter: true
# member with 1 month subscription (plan 2/standard)
user_3:
id: 3
username: pdurand
@ -94,6 +97,7 @@ user_3:
merged_at:
is_allow_newsletter: false
# member with 1 month subscription (plan 3/students)
user_4:
id: 4
username: kdumas
@ -126,6 +130,7 @@ user_4:
merged_at:
is_allow_newsletter: false
# member with 10€ on wallet
user_5:
id: 5
username: vlonchamp
@ -158,6 +163,7 @@ user_5:
merged_at:
is_allow_newsletter: true
# partner of plan 2
user_6:
id: 6
username: GilbertPartenaire
@ -190,6 +196,7 @@ user_6:
merged_at:
is_allow_newsletter: true
# member with 255€ on wallet
user_7:
id: 7
username: lseguin
@ -286,6 +293,7 @@ user_9:
merged_at:
is_allow_newsletter: true
# member with 1 year subscription
user_10:
id: 10
username: acamus

View File

@ -72,7 +72,7 @@ class Reservations::RestrictedTest < ActionDispatch::IntegrationTest
}.to_json, headers: default_headers
end
assert_equal 201, response.status
assert_equal 201, response.status, response.body
assert_equal reservations_count + 1, Reservation.count
assert_equal invoices_count + 1, Invoice.count
@ -105,7 +105,7 @@ class Reservations::RestrictedTest < ActionDispatch::IntegrationTest
}
}
assert_equal 201, response.status
assert_equal 201, response.status, response.body
# Check the id
availability = json_response(response.body)
@ -141,7 +141,7 @@ class Reservations::RestrictedTest < ActionDispatch::IntegrationTest
}.to_json, headers: default_headers
end
assert_equal 422, response.status
assert_equal 422, response.status, response.body
assert_match(/availability is restricted for subscribers/, response.body)
assert_equal reservations_count, Reservation.count
@ -175,7 +175,7 @@ class Reservations::RestrictedTest < ActionDispatch::IntegrationTest
}
}
assert_equal 201, response.status
assert_equal 201, response.status, response.body
# Check the id
availability = json_response(response.body)
@ -187,27 +187,25 @@ class Reservations::RestrictedTest < ActionDispatch::IntegrationTest
slot = Availability.find(availability[:id]).slots.first
# book a reservation
VCR.use_cassette('reservations_create_for_restricted_slot_forced') do
post '/api/local_payment/confirm_payment',
params: {
customer_id: @jdupont.id,
items: [
{
reservation: {
reservable_id: 2,
reservable_type: 'Machine',
slots_reservations_attributes: [
{
slot_id: slot.id
}
]
}
post '/api/local_payment/confirm_payment',
params: {
customer_id: @jdupont.id,
items: [
{
reservation: {
reservable_id: 2,
reservable_type: 'Machine',
slots_reservations_attributes: [
{
slot_id: slot.id
}
]
}
]
}.to_json, headers: default_headers
end
}
]
}.to_json, headers: default_headers
assert_equal 201, response.status
assert_equal 201, response.status, response.body
# Check the result
result = json_response(response.body)
@ -246,7 +244,7 @@ class Reservations::RestrictedTest < ActionDispatch::IntegrationTest
}
}
assert_equal 201, response.status
assert_equal 201, response.status, response.body
# Check the id
availability = json_response(response.body)
@ -287,7 +285,7 @@ class Reservations::RestrictedTest < ActionDispatch::IntegrationTest
}.to_json, headers: default_headers
end
assert_equal 201, response.status
assert_equal 201, response.status, response.body
# Check the result
result = json_response(response.body)

View File

@ -3,7 +3,7 @@
require 'test_helper'
# Test the service returning the availabilities for the given resources
class AvailabilitiesServiceTest < ActiveSupport::TestCase
class Availabilities::AvailabilitiesServiceTest < ActiveSupport::TestCase
setup do
@no_subscription = User.find_by(username: 'jdupond')
@with_subscription = User.find_by(username: 'kdumas')

View File

@ -0,0 +1,130 @@
# frozen_string_literal: true
require 'test_helper'
# Test the service returning the visibility window for availabilities
class Availabilities::VisibilityServiceTest < ActiveSupport::TestCase
setup do
@admin = User.find_by(username: 'admin')
@no_subscription = User.find_by(username: 'jdupond')
@with_subscription = User.find_by(username: 'kdumas')
@with_1y_subscription = User.find_by(username: 'acamus')
# from the fixtures:
# - visibility_others = 1 month
# - visibility_yearly = 3 months
end
test 'admin visibility for the coming month' do
starting = Time.current.beginning_of_day
ending = 1.month.from_now.end_of_day
window = Availabilities::VisibilityService.new.visibility(@admin, 'space', starting, ending)
assert_equal starting, window[0]
assert_equal ending, window[1]
end
test 'admin visibility for the previous month' do
starting = 1.month.ago.end_of_day
ending = Time.current.beginning_of_day
window = Availabilities::VisibilityService.new.visibility(@admin, 'space', starting, ending)
assert_equal starting, window[0]
assert_equal ending, window[1]
end
test 'admin visibility for the coming year' do
starting = Time.current.beginning_of_day
ending = 1.year.from_now.end_of_day
window = Availabilities::VisibilityService.new.visibility(@admin, 'space', starting, ending)
assert_equal starting, window[0]
assert_equal ending, window[1]
end
test 'member visibility for the coming month' do
starting = Time.current.beginning_of_day
ending = 1.month.from_now.end_of_day
window = Availabilities::VisibilityService.new.visibility(@no_subscription, 'space', starting, ending)
assert_datetimes_equal Time.current, window[0]
assert_datetimes_equal 1.month.from_now, window[1]
end
test 'member visibility for the previous month' do
starting = 1.month.ago.end_of_day
ending = Time.current.beginning_of_day
window = Availabilities::VisibilityService.new.visibility(@no_subscription, 'space', starting, ending)
assert_datetimes_equal Time.current, window[0]
assert_equal ending, window[1]
end
test 'member visibility for the coming year' do
starting = Time.current.beginning_of_day
ending = 1.year.from_now.end_of_day
window = Availabilities::VisibilityService.new.visibility(@no_subscription, 'space', starting, ending)
assert_datetimes_equal Time.current, window[0]
assert_datetimes_equal 1.month.from_now, window[1]
end
test 'subscriber visibility for the coming month' do
starting = Time.current.beginning_of_day
ending = 1.month.from_now.end_of_day
window = Availabilities::VisibilityService.new.visibility(@with_subscription, 'space', starting, ending)
assert_datetimes_equal Time.current, window[0]
assert_datetimes_equal 1.month.from_now, window[1]
end
test 'subscriber visibility for the previous month' do
starting = 1.month.ago.end_of_day
ending = Time.current.beginning_of_day
window = Availabilities::VisibilityService.new.visibility(@with_subscription, 'space', starting, ending)
assert_datetimes_equal Time.current, window[0]
assert_equal ending, window[1]
end
test 'subscriber visibility for the coming year' do
starting = Time.current.beginning_of_day
ending = 1.year.from_now.end_of_day
window = Availabilities::VisibilityService.new.visibility(@with_subscription, 'space', starting, ending)
assert_datetimes_equal Time.current, window[0]
assert_datetimes_equal 1.month.from_now, window[1]
end
test '1 year subscriber visibility for the coming month' do
starting = Time.current.beginning_of_day
ending = 1.month.from_now.end_of_day
window = Availabilities::VisibilityService.new.visibility(@with_1y_subscription, 'space', starting, ending)
assert_datetimes_equal Time.current, window[0]
assert_equal ending, window[1]
end
test '1 year subscriber visibility for the previous month' do
starting = 1.month.ago.end_of_day
ending = Time.current.beginning_of_day
window = Availabilities::VisibilityService.new.visibility(@with_1y_subscription, 'space', starting, ending)
assert_datetimes_equal Time.current, window[0]
assert_equal ending, window[1]
end
test '1 year subscriber visibility for the coming year' do
starting = Time.current.beginning_of_day
ending = 1.year.from_now.end_of_day
window = Availabilities::VisibilityService.new.visibility(@with_1y_subscription, 'space', starting, ending)
assert_datetimes_equal Time.current, window[0]
assert_datetimes_equal 3.months.from_now, window[1]
end
test '1 year subscriber visibility for trainings in the coming year' do
starting = Time.current.beginning_of_day
ending = 1.year.from_now.end_of_day
window = Availabilities::VisibilityService.new.visibility(@with_1y_subscription, 'training', starting, ending)
assert_datetimes_equal Time.current, window[0]
assert_datetimes_equal 1.month.from_now, window[1]
end
test 'subscriber with plan custom visibility' do
plan = @with_subscription.subscribed_plan
plan.update(machines_visibility: 48)
starting = Time.current.beginning_of_day
ending = 1.month.from_now.end_of_day
window = Availabilities::VisibilityService.new.visibility(@with_subscription, 'machine', starting, ending)
assert_datetimes_equal Time.current, window[0]
assert_datetimes_equal 48.hours.from_now, window[1]
end
end

View File

@ -111,6 +111,11 @@ class ActiveSupport::TestCase
assert_not_nil actual, msg
assert_equal expected.to_date, actual.to_date, msg
end
def assert_datetimes_equal(expected, actual, msg = nil)
assert_not_nil actual, msg
assert_equal expected.iso8601, actual.iso8601, msg
end
end
class ActionDispatch::IntegrationTest

View File

@ -5,7 +5,7 @@ http_interactions:
uri: https://api.stripe.com/v1/payment_methods
body:
encoding: UTF-8
string: type=card&card[number]=4242424242424242&card[exp_month]=4&card[exp_year]=2022&card[cvc]=314
string: type=card&card[number]=4242424242424242&card[exp_month]=4&card[exp_year]=2024&card[cvc]=314
headers:
User-Agent:
- Stripe/v1 RubyBindings/5.29.0
@ -13,12 +13,14 @@ http_interactions:
- Bearer sk_test_testfaketestfaketestfake
Content-Type:
- application/x-www-form-urlencoded
X-Stripe-Client-Telemetry:
- '{"last_request_metrics":{"request_id":"req_MQd4Z7i8cW9FYF","request_duration_ms":535}}'
Stripe-Version:
- '2019-08-14'
X-Stripe-Client-User-Agent:
- '{"bindings_version":"5.29.0","lang":"ruby","lang_version":"2.6.3 p62 (2019-04-16)","platform":"x86_64-darwin18","engine":"ruby","publisher":"stripe","uname":"Darwin
MacBook-Pro-Sleede-Peng 20.5.0 Darwin Kernel Version 20.5.0: Sat May 8 05:10:33
PDT 2021; root:xnu-7195.121.3~9/RELEASE_X86_64 x86_64","hostname":"MacBook-Pro-Sleede-Peng"}'
- '{"bindings_version":"5.29.0","lang":"ruby","lang_version":"2.6.10 p210 (2022-04-12)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux
version 6.2.5-arch1-1 (linux@archlinux) (gcc (GCC) 12.2.1 20230201, GNU ld
(GNU Binutils) 2.40) #1 SMP PREEMPT_DYNAMIC Sat, 11 Mar 2023 14:28:13 +0000","hostname":"Sylvain-desktop"}'
Accept-Encoding:
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept:
@ -31,11 +33,11 @@ http_interactions:
Server:
- nginx
Date:
- Mon, 13 Sep 2021 11:24:07 GMT
- Wed, 15 Mar 2023 11:51:50 GMT
Content-Type:
- application/json
Content-Length:
- '934'
- '930'
Connection:
- keep-alive
Access-Control-Allow-Credentials:
@ -50,19 +52,23 @@ http_interactions:
- '300'
Cache-Control:
- no-cache, no-store
Idempotency-Key:
- 8b19d06b-ed60-406d-8490-3d7062d47f67
Original-Request:
- req_jrCbj5YQwrn3m7
Request-Id:
- req_tlTlxEJC4LyAQv
- req_jrCbj5YQwrn3m7
Stripe-Should-Retry:
- 'false'
Stripe-Version:
- '2019-08-14'
X-Stripe-C-Cost:
- '6'
Strict-Transport-Security:
- max-age=31556926; includeSubDomains; preload
- max-age=63072000; includeSubDomains; preload
body:
encoding: UTF-8
string: |
string: |-
{
"id": "pm_1JZDGd2sOmf47Nz9LCckU76B",
"id": "pm_1Mlsry2sOmf47Nz9g8twwVyn",
"object": "payment_method",
"billing_details": {
"address": {
@ -86,7 +92,7 @@ http_interactions:
},
"country": "US",
"exp_month": 4,
"exp_year": 2022,
"exp_year": 2024,
"fingerprint": "o52jybR7bnmNn6AT",
"funding": "credit",
"generated_from": null,
@ -102,20 +108,19 @@ http_interactions:
},
"wallet": null
},
"created": 1631532247,
"created": 1678881110,
"customer": null,
"livemode": false,
"metadata": {
},
"metadata": {},
"type": "card"
}
recorded_at: Mon, 13 Sep 2021 11:24:07 GMT
recorded_at: Wed, 15 Mar 2023 11:51:50 GMT
- request:
method: post
uri: https://api.stripe.com/v1/payment_intents
body:
encoding: UTF-8
string: payment_method=pm_1JZDGd2sOmf47Nz9LCckU76B&amount=11500&currency=usd&confirmation_method=manual&confirm=true&customer=cus_8Di1wjdVktv5kt
string: payment_method=pm_1Mlsry2sOmf47Nz9g8twwVyn&amount=11500&currency=usd&confirmation_method=manual&confirm=true&customer=cus_8Di1wjdVktv5kt
headers:
User-Agent:
- Stripe/v1 RubyBindings/5.29.0
@ -124,13 +129,13 @@ http_interactions:
Content-Type:
- application/x-www-form-urlencoded
X-Stripe-Client-Telemetry:
- '{"last_request_metrics":{"request_id":"req_tlTlxEJC4LyAQv","request_duration_ms":663}}'
- '{"last_request_metrics":{"request_id":"req_jrCbj5YQwrn3m7","request_duration_ms":586}}'
Stripe-Version:
- '2019-08-14'
X-Stripe-Client-User-Agent:
- '{"bindings_version":"5.29.0","lang":"ruby","lang_version":"2.6.3 p62 (2019-04-16)","platform":"x86_64-darwin18","engine":"ruby","publisher":"stripe","uname":"Darwin
MacBook-Pro-Sleede-Peng 20.5.0 Darwin Kernel Version 20.5.0: Sat May 8 05:10:33
PDT 2021; root:xnu-7195.121.3~9/RELEASE_X86_64 x86_64","hostname":"MacBook-Pro-Sleede-Peng"}'
- '{"bindings_version":"5.29.0","lang":"ruby","lang_version":"2.6.10 p210 (2022-04-12)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux
version 6.2.5-arch1-1 (linux@archlinux) (gcc (GCC) 12.2.1 20230201, GNU ld
(GNU Binutils) 2.40) #1 SMP PREEMPT_DYNAMIC Sat, 11 Mar 2023 14:28:13 +0000","hostname":"Sylvain-desktop"}'
Accept-Encoding:
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept:
@ -143,11 +148,11 @@ http_interactions:
Server:
- nginx
Date:
- Mon, 13 Sep 2021 11:24:09 GMT
- Wed, 15 Mar 2023 11:51:52 GMT
Content-Type:
- application/json
Content-Length:
- '4263'
- '4522'
Connection:
- keep-alive
Access-Control-Allow-Credentials:
@ -162,25 +167,33 @@ http_interactions:
- '300'
Cache-Control:
- no-cache, no-store
Idempotency-Key:
- bceb9ff3-545c-48c9-9f9b-4654dd50641b
Original-Request:
- req_2QEtRciNfNblB1
Request-Id:
- req_4hAutJ5WkAA9ps
- req_2QEtRciNfNblB1
Stripe-Should-Retry:
- 'false'
Stripe-Version:
- '2019-08-14'
X-Stripe-C-Cost:
- '10'
Strict-Transport-Security:
- max-age=31556926; includeSubDomains; preload
- max-age=63072000; includeSubDomains; preload
body:
encoding: UTF-8
string: |
string: |-
{
"id": "pi_3JZDGd2sOmf47Nz91tgWkK3L",
"id": "pi_3Mlsrz2sOmf47Nz90UCTYKFx",
"object": "payment_intent",
"amount": 11500,
"amount_capturable": 0,
"amount_details": {
"tip": {}
},
"amount_received": 11500,
"application": null,
"application_fee_amount": null,
"automatic_payment_methods": null,
"canceled_at": null,
"cancellation_reason": null,
"capture_method": "automatic",
@ -188,7 +201,7 @@ http_interactions:
"object": "list",
"data": [
{
"id": "ch_3JZDGd2sOmf47Nz91HsbEa5U",
"id": "ch_3Mlsrz2sOmf47Nz9004R5HME",
"object": "charge",
"amount": 11500,
"amount_captured": 11500,
@ -196,7 +209,7 @@ http_interactions:
"application": null,
"application_fee": null,
"application_fee_amount": null,
"balance_transaction": "txn_3JZDGd2sOmf47Nz91MEnox3F",
"balance_transaction": "txn_3Mlsrz2sOmf47Nz90SLqRLy1",
"billing_details": {
"address": {
"city": null,
@ -212,34 +225,33 @@ http_interactions:
},
"calculated_statement_descriptor": "Stripe",
"captured": true,
"created": 1631532248,
"created": 1678881111,
"currency": "usd",
"customer": "cus_8Di1wjdVktv5kt",
"description": null,
"destination": null,
"dispute": null,
"disputed": false,
"failure_balance_transaction": null,
"failure_code": null,
"failure_message": null,
"fraud_details": {
},
"fraud_details": {},
"invoice": null,
"livemode": false,
"metadata": {
},
"metadata": {},
"on_behalf_of": null,
"order": null,
"outcome": {
"network_status": "approved_by_network",
"reason": null,
"risk_level": "normal",
"risk_score": 12,
"risk_score": 29,
"seller_message": "Payment complete.",
"type": "authorized"
},
"paid": true,
"payment_intent": "pi_3JZDGd2sOmf47Nz91tgWkK3L",
"payment_method": "pm_1JZDGd2sOmf47Nz9LCckU76B",
"payment_intent": "pi_3Mlsrz2sOmf47Nz90UCTYKFx",
"payment_method": "pm_1Mlsry2sOmf47Nz9g8twwVyn",
"payment_method_details": {
"card": {
"brand": "visa",
@ -250,11 +262,12 @@ http_interactions:
},
"country": "US",
"exp_month": 4,
"exp_year": 2022,
"exp_year": 2024,
"fingerprint": "o52jybR7bnmNn6AT",
"funding": "credit",
"installments": null,
"last4": "4242",
"mandate": null,
"network": "visa",
"three_d_secure": null,
"wallet": null
@ -263,16 +276,14 @@ http_interactions:
},
"receipt_email": null,
"receipt_number": null,
"receipt_url": "https://pay.stripe.com/receipts/acct_103rE62sOmf47Nz9/ch_3JZDGd2sOmf47Nz91HsbEa5U/rcpt_KDeZXFrj8mqhXX4v6MKFwawzylc2kPA",
"receipt_url": "https://pay.stripe.com/receipts/payment/CAcaFwoVYWNjdF8xMDNyRTYyc09tZjQ3Tno5KNjixqAGMgZncKXPXn06LBY8nUVkDGBhMzC5bIzAPJWsR_S75nAIhMIDBqALhrU35BgWxXMZpcMss_ty",
"refunded": false,
"refunds": {
"object": "list",
"data": [
],
"data": [],
"has_more": false,
"total_count": 0,
"url": "/v1/charges/ch_3JZDGd2sOmf47Nz91HsbEa5U/refunds"
"url": "/v1/charges/ch_3Mlsrz2sOmf47Nz9004R5HME/refunds"
},
"review": null,
"shipping": null,
@ -287,25 +298,26 @@ http_interactions:
],
"has_more": false,
"total_count": 1,
"url": "/v1/charges?payment_intent=pi_3JZDGd2sOmf47Nz91tgWkK3L"
"url": "/v1/charges?payment_intent=pi_3Mlsrz2sOmf47Nz90UCTYKFx"
},
"client_secret": "pi_3JZDGd2sOmf47Nz91tgWkK3L_secret_DooP6j5YiNN0kzaXPdTGEeKeR",
"client_secret": "pi_3Mlsrz2sOmf47Nz90UCTYKFx_secret_af9V81CCyVZZ2J2sptymbRLES",
"confirmation_method": "manual",
"created": 1631532247,
"created": 1678881111,
"currency": "usd",
"customer": "cus_8Di1wjdVktv5kt",
"description": null,
"invoice": null,
"last_payment_error": null,
"latest_charge": "ch_3Mlsrz2sOmf47Nz9004R5HME",
"livemode": false,
"metadata": {
},
"metadata": {},
"next_action": null,
"on_behalf_of": null,
"payment_method": "pm_1JZDGd2sOmf47Nz9LCckU76B",
"payment_method": "pm_1Mlsry2sOmf47Nz9g8twwVyn",
"payment_method_options": {
"card": {
"installments": null,
"mandate_options": null,
"network": null,
"request_three_d_secure": "automatic"
}
@ -313,6 +325,7 @@ http_interactions:
"payment_method_types": [
"card"
],
"processing": null,
"receipt_email": null,
"review": null,
"setup_future_usage": null,
@ -324,13 +337,13 @@ http_interactions:
"transfer_data": null,
"transfer_group": null
}
recorded_at: Mon, 13 Sep 2021 11:24:09 GMT
recorded_at: Wed, 15 Mar 2023 11:51:52 GMT
- request:
method: post
uri: https://api.stripe.com/v1/payment_intents/pi_3JZDGd2sOmf47Nz91tgWkK3L
uri: https://api.stripe.com/v1/payment_intents/pi_3Mlsrz2sOmf47Nz90UCTYKFx
body:
encoding: UTF-8
string: description=Invoice+reference%3A+2109001%2FVL
string: description=Invoice+reference%3A+2303007%2FVL
headers:
User-Agent:
- Stripe/v1 RubyBindings/5.29.0
@ -339,13 +352,13 @@ http_interactions:
Content-Type:
- application/x-www-form-urlencoded
X-Stripe-Client-Telemetry:
- '{"last_request_metrics":{"request_id":"req_4hAutJ5WkAA9ps","request_duration_ms":1725}}'
- '{"last_request_metrics":{"request_id":"req_2QEtRciNfNblB1","request_duration_ms":1482}}'
Stripe-Version:
- '2019-08-14'
X-Stripe-Client-User-Agent:
- '{"bindings_version":"5.29.0","lang":"ruby","lang_version":"2.6.3 p62 (2019-04-16)","platform":"x86_64-darwin18","engine":"ruby","publisher":"stripe","uname":"Darwin
MacBook-Pro-Sleede-Peng 20.5.0 Darwin Kernel Version 20.5.0: Sat May 8 05:10:33
PDT 2021; root:xnu-7195.121.3~9/RELEASE_X86_64 x86_64","hostname":"MacBook-Pro-Sleede-Peng"}'
- '{"bindings_version":"5.29.0","lang":"ruby","lang_version":"2.6.10 p210 (2022-04-12)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux
version 6.2.5-arch1-1 (linux@archlinux) (gcc (GCC) 12.2.1 20230201, GNU ld
(GNU Binutils) 2.40) #1 SMP PREEMPT_DYNAMIC Sat, 11 Mar 2023 14:28:13 +0000","hostname":"Sylvain-desktop"}'
Accept-Encoding:
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept:
@ -358,11 +371,11 @@ http_interactions:
Server:
- nginx
Date:
- Mon, 13 Sep 2021 11:24:10 GMT
- Wed, 15 Mar 2023 11:51:52 GMT
Content-Type:
- application/json
Content-Length:
- '4290'
- '4549'
Connection:
- keep-alive
Access-Control-Allow-Credentials:
@ -377,25 +390,33 @@ http_interactions:
- '300'
Cache-Control:
- no-cache, no-store
Idempotency-Key:
- 51790c68-63e7-4b6e-96b1-4b6eef30b42b
Original-Request:
- req_Ms4raQ174jWmPK
Request-Id:
- req_ysWG3JfyCp5xVD
- req_Ms4raQ174jWmPK
Stripe-Should-Retry:
- 'false'
Stripe-Version:
- '2019-08-14'
X-Stripe-C-Cost:
- '0'
Strict-Transport-Security:
- max-age=31556926; includeSubDomains; preload
- max-age=63072000; includeSubDomains; preload
body:
encoding: UTF-8
string: |
string: |-
{
"id": "pi_3JZDGd2sOmf47Nz91tgWkK3L",
"id": "pi_3Mlsrz2sOmf47Nz90UCTYKFx",
"object": "payment_intent",
"amount": 11500,
"amount_capturable": 0,
"amount_details": {
"tip": {}
},
"amount_received": 11500,
"application": null,
"application_fee_amount": null,
"automatic_payment_methods": null,
"canceled_at": null,
"cancellation_reason": null,
"capture_method": "automatic",
@ -403,7 +424,7 @@ http_interactions:
"object": "list",
"data": [
{
"id": "ch_3JZDGd2sOmf47Nz91HsbEa5U",
"id": "ch_3Mlsrz2sOmf47Nz9004R5HME",
"object": "charge",
"amount": 11500,
"amount_captured": 11500,
@ -411,7 +432,7 @@ http_interactions:
"application": null,
"application_fee": null,
"application_fee_amount": null,
"balance_transaction": "txn_3JZDGd2sOmf47Nz91MEnox3F",
"balance_transaction": "txn_3Mlsrz2sOmf47Nz90SLqRLy1",
"billing_details": {
"address": {
"city": null,
@ -427,34 +448,33 @@ http_interactions:
},
"calculated_statement_descriptor": "Stripe",
"captured": true,
"created": 1631532248,
"created": 1678881111,
"currency": "usd",
"customer": "cus_8Di1wjdVktv5kt",
"description": null,
"destination": null,
"dispute": null,
"disputed": false,
"failure_balance_transaction": null,
"failure_code": null,
"failure_message": null,
"fraud_details": {
},
"fraud_details": {},
"invoice": null,
"livemode": false,
"metadata": {
},
"metadata": {},
"on_behalf_of": null,
"order": null,
"outcome": {
"network_status": "approved_by_network",
"reason": null,
"risk_level": "normal",
"risk_score": 12,
"risk_score": 29,
"seller_message": "Payment complete.",
"type": "authorized"
},
"paid": true,
"payment_intent": "pi_3JZDGd2sOmf47Nz91tgWkK3L",
"payment_method": "pm_1JZDGd2sOmf47Nz9LCckU76B",
"payment_intent": "pi_3Mlsrz2sOmf47Nz90UCTYKFx",
"payment_method": "pm_1Mlsry2sOmf47Nz9g8twwVyn",
"payment_method_details": {
"card": {
"brand": "visa",
@ -465,11 +485,12 @@ http_interactions:
},
"country": "US",
"exp_month": 4,
"exp_year": 2022,
"exp_year": 2024,
"fingerprint": "o52jybR7bnmNn6AT",
"funding": "credit",
"installments": null,
"last4": "4242",
"mandate": null,
"network": "visa",
"three_d_secure": null,
"wallet": null
@ -478,16 +499,14 @@ http_interactions:
},
"receipt_email": null,
"receipt_number": null,
"receipt_url": "https://pay.stripe.com/receipts/acct_103rE62sOmf47Nz9/ch_3JZDGd2sOmf47Nz91HsbEa5U/rcpt_KDeZXFrj8mqhXX4v6MKFwawzylc2kPA",
"receipt_url": "https://pay.stripe.com/receipts/payment/CAcaFwoVYWNjdF8xMDNyRTYyc09tZjQ3Tno5KNjixqAGMgbuQcWgG5c6LBa-ZSU_-3ZZaF7tt6uBsD2Rs604Am5ssLMpXbt0FvRIOVw-pVEwxmP_5ACg",
"refunded": false,
"refunds": {
"object": "list",
"data": [
],
"data": [],
"has_more": false,
"total_count": 0,
"url": "/v1/charges/ch_3JZDGd2sOmf47Nz91HsbEa5U/refunds"
"url": "/v1/charges/ch_3Mlsrz2sOmf47Nz9004R5HME/refunds"
},
"review": null,
"shipping": null,
@ -502,25 +521,26 @@ http_interactions:
],
"has_more": false,
"total_count": 1,
"url": "/v1/charges?payment_intent=pi_3JZDGd2sOmf47Nz91tgWkK3L"
"url": "/v1/charges?payment_intent=pi_3Mlsrz2sOmf47Nz90UCTYKFx"
},
"client_secret": "pi_3JZDGd2sOmf47Nz91tgWkK3L_secret_DooP6j5YiNN0kzaXPdTGEeKeR",
"client_secret": "pi_3Mlsrz2sOmf47Nz90UCTYKFx_secret_af9V81CCyVZZ2J2sptymbRLES",
"confirmation_method": "manual",
"created": 1631532247,
"created": 1678881111,
"currency": "usd",
"customer": "cus_8Di1wjdVktv5kt",
"description": "Invoice reference: 2109001/VL",
"description": "Invoice reference: 2303007/VL",
"invoice": null,
"last_payment_error": null,
"latest_charge": "ch_3Mlsrz2sOmf47Nz9004R5HME",
"livemode": false,
"metadata": {
},
"metadata": {},
"next_action": null,
"on_behalf_of": null,
"payment_method": "pm_1JZDGd2sOmf47Nz9LCckU76B",
"payment_method": "pm_1Mlsry2sOmf47Nz9g8twwVyn",
"payment_method_options": {
"card": {
"installments": null,
"mandate_options": null,
"network": null,
"request_three_d_secure": "automatic"
}
@ -528,6 +548,7 @@ http_interactions:
"payment_method_types": [
"card"
],
"processing": null,
"receipt_email": null,
"review": null,
"setup_future_usage": null,
@ -539,5 +560,5 @@ http_interactions:
"transfer_data": null,
"transfer_group": null
}
recorded_at: Mon, 13 Sep 2021 11:24:10 GMT
recorded_at: Wed, 15 Mar 2023 11:51:53 GMT
recorded_with: VCR 6.0.0

View File

@ -5,7 +5,7 @@ http_interactions:
uri: https://api.stripe.com/v1/payment_methods
body:
encoding: UTF-8
string: type=card&card[number]=4242424242424242&card[exp_month]=4&card[exp_year]=2022&card[cvc]=314
string: type=card&card[number]=4242424242424242&card[exp_month]=4&card[exp_year]=2024&card[cvc]=314
headers:
User-Agent:
- Stripe/v1 RubyBindings/5.29.0
@ -13,14 +13,12 @@ http_interactions:
- Bearer sk_test_testfaketestfaketestfake
Content-Type:
- application/x-www-form-urlencoded
X-Stripe-Client-Telemetry:
- '{"last_request_metrics":{"request_id":"req_ysWG3JfyCp5xVD","request_duration_ms":520}}'
Stripe-Version:
- '2019-08-14'
X-Stripe-Client-User-Agent:
- '{"bindings_version":"5.29.0","lang":"ruby","lang_version":"2.6.3 p62 (2019-04-16)","platform":"x86_64-darwin18","engine":"ruby","publisher":"stripe","uname":"Darwin
MacBook-Pro-Sleede-Peng 20.5.0 Darwin Kernel Version 20.5.0: Sat May 8 05:10:33
PDT 2021; root:xnu-7195.121.3~9/RELEASE_X86_64 x86_64","hostname":"MacBook-Pro-Sleede-Peng"}'
- '{"bindings_version":"5.29.0","lang":"ruby","lang_version":"2.6.10 p210 (2022-04-12)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux
version 6.2.5-arch1-1 (linux@archlinux) (gcc (GCC) 12.2.1 20230201, GNU ld
(GNU Binutils) 2.40) #1 SMP PREEMPT_DYNAMIC Sat, 11 Mar 2023 14:28:13 +0000","hostname":"Sylvain-desktop"}'
Accept-Encoding:
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept:
@ -33,11 +31,11 @@ http_interactions:
Server:
- nginx
Date:
- Mon, 13 Sep 2021 11:24:10 GMT
- Wed, 15 Mar 2023 11:51:20 GMT
Content-Type:
- application/json
Content-Length:
- '934'
- '930'
Connection:
- keep-alive
Access-Control-Allow-Credentials:
@ -52,19 +50,23 @@ http_interactions:
- '300'
Cache-Control:
- no-cache, no-store
Idempotency-Key:
- 6a183d91-3220-4ad4-913c-f169a75aa488
Original-Request:
- req_LQKka6p7rniNKT
Request-Id:
- req_7RNGSU2vySHdHz
- req_LQKka6p7rniNKT
Stripe-Should-Retry:
- 'false'
Stripe-Version:
- '2019-08-14'
X-Stripe-C-Cost:
- '6'
Strict-Transport-Security:
- max-age=31556926; includeSubDomains; preload
- max-age=63072000; includeSubDomains; preload
body:
encoding: UTF-8
string: |
string: |-
{
"id": "pm_1JZDGg2sOmf47Nz9pfmMaPtb",
"id": "pm_1MlsrU2sOmf47Nz9voyfBlTb",
"object": "payment_method",
"billing_details": {
"address": {
@ -88,7 +90,7 @@ http_interactions:
},
"country": "US",
"exp_month": 4,
"exp_year": 2022,
"exp_year": 2024,
"fingerprint": "o52jybR7bnmNn6AT",
"funding": "credit",
"generated_from": null,
@ -104,227 +106,11 @@ http_interactions:
},
"wallet": null
},
"created": 1631532250,
"created": 1678881080,
"customer": null,
"livemode": false,
"metadata": {
},
"metadata": {},
"type": "card"
}
recorded_at: Mon, 13 Sep 2021 11:24:10 GMT
- request:
method: post
uri: https://api.stripe.com/v1/payment_intents
body:
encoding: UTF-8
string: payment_method=pm_1JZDGg2sOmf47Nz9pfmMaPtb&amount=4200&currency=usd&confirmation_method=manual&confirm=true&customer=cus_8Di1wjdVktv5kt
headers:
User-Agent:
- Stripe/v1 RubyBindings/5.29.0
Authorization:
- Bearer sk_test_testfaketestfaketestfake
Content-Type:
- application/x-www-form-urlencoded
X-Stripe-Client-Telemetry:
- '{"last_request_metrics":{"request_id":"req_7RNGSU2vySHdHz","request_duration_ms":628}}'
Stripe-Version:
- '2019-08-14'
X-Stripe-Client-User-Agent:
- '{"bindings_version":"5.29.0","lang":"ruby","lang_version":"2.6.3 p62 (2019-04-16)","platform":"x86_64-darwin18","engine":"ruby","publisher":"stripe","uname":"Darwin
MacBook-Pro-Sleede-Peng 20.5.0 Darwin Kernel Version 20.5.0: Sat May 8 05:10:33
PDT 2021; root:xnu-7195.121.3~9/RELEASE_X86_64 x86_64","hostname":"MacBook-Pro-Sleede-Peng"}'
Accept-Encoding:
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept:
- "*/*"
response:
status:
code: 200
message: OK
headers:
Server:
- nginx
Date:
- Mon, 13 Sep 2021 11:24:12 GMT
Content-Type:
- application/json
Content-Length:
- '4258'
Connection:
- keep-alive
Access-Control-Allow-Credentials:
- 'true'
Access-Control-Allow-Methods:
- GET, POST, HEAD, OPTIONS, DELETE
Access-Control-Allow-Origin:
- "*"
Access-Control-Expose-Headers:
- Request-Id, Stripe-Manage-Version, X-Stripe-External-Auth-Required, X-Stripe-Privileged-Session-Required
Access-Control-Max-Age:
- '300'
Cache-Control:
- no-cache, no-store
Request-Id:
- req_NgGOJxEFd8THv8
Stripe-Version:
- '2019-08-14'
X-Stripe-C-Cost:
- '10'
Strict-Transport-Security:
- max-age=31556926; includeSubDomains; preload
body:
encoding: UTF-8
string: |
{
"id": "pi_3JZDGh2sOmf47Nz91FT4yZ2t",
"object": "payment_intent",
"amount": 4200,
"amount_capturable": 0,
"amount_received": 4200,
"application": null,
"application_fee_amount": null,
"canceled_at": null,
"cancellation_reason": null,
"capture_method": "automatic",
"charges": {
"object": "list",
"data": [
{
"id": "ch_3JZDGh2sOmf47Nz91FoAsBFe",
"object": "charge",
"amount": 4200,
"amount_captured": 4200,
"amount_refunded": 0,
"application": null,
"application_fee": null,
"application_fee_amount": null,
"balance_transaction": "txn_3JZDGh2sOmf47Nz91yuRvukb",
"billing_details": {
"address": {
"city": null,
"country": null,
"line1": null,
"line2": null,
"postal_code": null,
"state": null
},
"email": null,
"name": null,
"phone": null
},
"calculated_statement_descriptor": "Stripe",
"captured": true,
"created": 1631532251,
"currency": "usd",
"customer": "cus_8Di1wjdVktv5kt",
"description": null,
"destination": null,
"dispute": null,
"disputed": false,
"failure_code": null,
"failure_message": null,
"fraud_details": {
},
"invoice": null,
"livemode": false,
"metadata": {
},
"on_behalf_of": null,
"order": null,
"outcome": {
"network_status": "approved_by_network",
"reason": null,
"risk_level": "normal",
"risk_score": 2,
"seller_message": "Payment complete.",
"type": "authorized"
},
"paid": true,
"payment_intent": "pi_3JZDGh2sOmf47Nz91FT4yZ2t",
"payment_method": "pm_1JZDGg2sOmf47Nz9pfmMaPtb",
"payment_method_details": {
"card": {
"brand": "visa",
"checks": {
"address_line1_check": null,
"address_postal_code_check": null,
"cvc_check": "pass"
},
"country": "US",
"exp_month": 4,
"exp_year": 2022,
"fingerprint": "o52jybR7bnmNn6AT",
"funding": "credit",
"installments": null,
"last4": "4242",
"network": "visa",
"three_d_secure": null,
"wallet": null
},
"type": "card"
},
"receipt_email": null,
"receipt_number": null,
"receipt_url": "https://pay.stripe.com/receipts/acct_103rE62sOmf47Nz9/ch_3JZDGh2sOmf47Nz91FoAsBFe/rcpt_KDeZ4pRoBzCyvhebh2wUzvr5fmdZdtD",
"refunded": false,
"refunds": {
"object": "list",
"data": [
],
"has_more": false,
"total_count": 0,
"url": "/v1/charges/ch_3JZDGh2sOmf47Nz91FoAsBFe/refunds"
},
"review": null,
"shipping": null,
"source": null,
"source_transfer": null,
"statement_descriptor": null,
"statement_descriptor_suffix": null,
"status": "succeeded",
"transfer_data": null,
"transfer_group": null
}
],
"has_more": false,
"total_count": 1,
"url": "/v1/charges?payment_intent=pi_3JZDGh2sOmf47Nz91FT4yZ2t"
},
"client_secret": "pi_3JZDGh2sOmf47Nz91FT4yZ2t_secret_F3QBmBEtZjcaKblNLMUnLO6hD",
"confirmation_method": "manual",
"created": 1631532251,
"currency": "usd",
"customer": "cus_8Di1wjdVktv5kt",
"description": null,
"invoice": null,
"last_payment_error": null,
"livemode": false,
"metadata": {
},
"next_action": null,
"on_behalf_of": null,
"payment_method": "pm_1JZDGg2sOmf47Nz9pfmMaPtb",
"payment_method_options": {
"card": {
"installments": null,
"network": null,
"request_three_d_secure": "automatic"
}
},
"payment_method_types": [
"card"
],
"receipt_email": null,
"review": null,
"setup_future_usage": null,
"shipping": null,
"source": null,
"statement_descriptor": null,
"statement_descriptor_suffix": null,
"status": "succeeded",
"transfer_data": null,
"transfer_group": null
}
recorded_at: Mon, 13 Sep 2021 11:24:12 GMT
recorded_at: Wed, 15 Mar 2023 11:51:20 GMT
recorded_with: VCR 6.0.0

View File

@ -5,7 +5,7 @@ http_interactions:
uri: https://api.stripe.com/v1/payment_methods
body:
encoding: UTF-8
string: type=card&card[number]=4242424242424242&card[exp_month]=4&card[exp_year]=2022&card[cvc]=314
string: type=card&card[number]=4242424242424242&card[exp_month]=4&card[exp_year]=2024&card[cvc]=314
headers:
User-Agent:
- Stripe/v1 RubyBindings/5.29.0
@ -13,14 +13,12 @@ http_interactions:
- Bearer sk_test_testfaketestfaketestfake
Content-Type:
- application/x-www-form-urlencoded
X-Stripe-Client-Telemetry:
- '{"last_request_metrics":{"request_id":"req_NgGOJxEFd8THv8","request_duration_ms":1772}}'
Stripe-Version:
- '2019-08-14'
X-Stripe-Client-User-Agent:
- '{"bindings_version":"5.29.0","lang":"ruby","lang_version":"2.6.3 p62 (2019-04-16)","platform":"x86_64-darwin18","engine":"ruby","publisher":"stripe","uname":"Darwin
MacBook-Pro-Sleede-Peng 20.5.0 Darwin Kernel Version 20.5.0: Sat May 8 05:10:33
PDT 2021; root:xnu-7195.121.3~9/RELEASE_X86_64 x86_64","hostname":"MacBook-Pro-Sleede-Peng"}'
- '{"bindings_version":"5.29.0","lang":"ruby","lang_version":"2.6.10 p210 (2022-04-12)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux
version 6.2.5-arch1-1 (linux@archlinux) (gcc (GCC) 12.2.1 20230201, GNU ld
(GNU Binutils) 2.40) #1 SMP PREEMPT_DYNAMIC Sat, 11 Mar 2023 14:28:13 +0000","hostname":"Sylvain-desktop"}'
Accept-Encoding:
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept:
@ -33,11 +31,11 @@ http_interactions:
Server:
- nginx
Date:
- Mon, 13 Sep 2021 11:24:13 GMT
- Wed, 15 Mar 2023 11:51:46 GMT
Content-Type:
- application/json
Content-Length:
- '934'
- '930'
Connection:
- keep-alive
Access-Control-Allow-Credentials:
@ -52,19 +50,23 @@ http_interactions:
- '300'
Cache-Control:
- no-cache, no-store
Idempotency-Key:
- 4a6e0351-64bd-4ede-a018-7c03e320c3f5
Original-Request:
- req_njTGdMZNpa6wpG
Request-Id:
- req_px5zsAdlzgwwSe
- req_njTGdMZNpa6wpG
Stripe-Should-Retry:
- 'false'
Stripe-Version:
- '2019-08-14'
X-Stripe-C-Cost:
- '6'
Strict-Transport-Security:
- max-age=31556926; includeSubDomains; preload
- max-age=63072000; includeSubDomains; preload
body:
encoding: UTF-8
string: |
string: |-
{
"id": "pm_1JZDGj2sOmf47Nz9S8jhZkFt",
"id": "pm_1Mlsru2sOmf47Nz9s3VNHXYt",
"object": "payment_method",
"billing_details": {
"address": {
@ -88,7 +90,7 @@ http_interactions:
},
"country": "US",
"exp_month": 4,
"exp_year": 2022,
"exp_year": 2024,
"fingerprint": "o52jybR7bnmNn6AT",
"funding": "credit",
"generated_from": null,
@ -104,20 +106,19 @@ http_interactions:
},
"wallet": null
},
"created": 1631532253,
"created": 1678881106,
"customer": null,
"livemode": false,
"metadata": {
},
"metadata": {},
"type": "card"
}
recorded_at: Mon, 13 Sep 2021 11:24:13 GMT
recorded_at: Wed, 15 Mar 2023 11:51:46 GMT
- request:
method: post
uri: https://api.stripe.com/v1/payment_intents
body:
encoding: UTF-8
string: payment_method=pm_1JZDGj2sOmf47Nz9S8jhZkFt&amount=1500&currency=usd&confirmation_method=manual&confirm=true&customer=cus_8CzHcwBJtlA3IL
string: payment_method=pm_1Mlsru2sOmf47Nz9s3VNHXYt&amount=1500&currency=usd&confirmation_method=manual&confirm=true&customer=cus_8CzHcwBJtlA3IL
headers:
User-Agent:
- Stripe/v1 RubyBindings/5.29.0
@ -126,13 +127,13 @@ http_interactions:
Content-Type:
- application/x-www-form-urlencoded
X-Stripe-Client-Telemetry:
- '{"last_request_metrics":{"request_id":"req_px5zsAdlzgwwSe","request_duration_ms":619}}'
- '{"last_request_metrics":{"request_id":"req_njTGdMZNpa6wpG","request_duration_ms":581}}'
Stripe-Version:
- '2019-08-14'
X-Stripe-Client-User-Agent:
- '{"bindings_version":"5.29.0","lang":"ruby","lang_version":"2.6.3 p62 (2019-04-16)","platform":"x86_64-darwin18","engine":"ruby","publisher":"stripe","uname":"Darwin
MacBook-Pro-Sleede-Peng 20.5.0 Darwin Kernel Version 20.5.0: Sat May 8 05:10:33
PDT 2021; root:xnu-7195.121.3~9/RELEASE_X86_64 x86_64","hostname":"MacBook-Pro-Sleede-Peng"}'
- '{"bindings_version":"5.29.0","lang":"ruby","lang_version":"2.6.10 p210 (2022-04-12)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux
version 6.2.5-arch1-1 (linux@archlinux) (gcc (GCC) 12.2.1 20230201, GNU ld
(GNU Binutils) 2.40) #1 SMP PREEMPT_DYNAMIC Sat, 11 Mar 2023 14:28:13 +0000","hostname":"Sylvain-desktop"}'
Accept-Encoding:
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept:
@ -145,11 +146,11 @@ http_interactions:
Server:
- nginx
Date:
- Mon, 13 Sep 2021 11:24:15 GMT
- Wed, 15 Mar 2023 11:51:48 GMT
Content-Type:
- application/json
Content-Length:
- '4259'
- '4517'
Connection:
- keep-alive
Access-Control-Allow-Credentials:
@ -164,25 +165,33 @@ http_interactions:
- '300'
Cache-Control:
- no-cache, no-store
Idempotency-Key:
- f50a36a7-97b5-4d67-9c77-43b5cbddccbe
Original-Request:
- req_EocoIWBH6tucS4
Request-Id:
- req_EMDO1Z1Uux0kJb
- req_EocoIWBH6tucS4
Stripe-Should-Retry:
- 'false'
Stripe-Version:
- '2019-08-14'
X-Stripe-C-Cost:
- '10'
Strict-Transport-Security:
- max-age=31556926; includeSubDomains; preload
- max-age=63072000; includeSubDomains; preload
body:
encoding: UTF-8
string: |
string: |-
{
"id": "pi_3JZDGj2sOmf47Nz90n2aOsuM",
"id": "pi_3Mlsrv2sOmf47Nz91hRe49rq",
"object": "payment_intent",
"amount": 1500,
"amount_capturable": 0,
"amount_details": {
"tip": {}
},
"amount_received": 1500,
"application": null,
"application_fee_amount": null,
"automatic_payment_methods": null,
"canceled_at": null,
"cancellation_reason": null,
"capture_method": "automatic",
@ -190,7 +199,7 @@ http_interactions:
"object": "list",
"data": [
{
"id": "ch_3JZDGj2sOmf47Nz90qQzwGqL",
"id": "ch_3Mlsrv2sOmf47Nz91760imzX",
"object": "charge",
"amount": 1500,
"amount_captured": 1500,
@ -198,7 +207,7 @@ http_interactions:
"application": null,
"application_fee": null,
"application_fee_amount": null,
"balance_transaction": "txn_3JZDGj2sOmf47Nz90fOwfgv5",
"balance_transaction": "txn_3Mlsrv2sOmf47Nz915VOM7IM",
"billing_details": {
"address": {
"city": null,
@ -214,34 +223,33 @@ http_interactions:
},
"calculated_statement_descriptor": "Stripe",
"captured": true,
"created": 1631532254,
"created": 1678881107,
"currency": "usd",
"customer": "cus_8CzHcwBJtlA3IL",
"description": null,
"destination": null,
"dispute": null,
"disputed": false,
"failure_balance_transaction": null,
"failure_code": null,
"failure_message": null,
"fraud_details": {
},
"fraud_details": {},
"invoice": null,
"livemode": false,
"metadata": {
},
"metadata": {},
"on_behalf_of": null,
"order": null,
"outcome": {
"network_status": "approved_by_network",
"reason": null,
"risk_level": "normal",
"risk_score": 22,
"risk_score": 7,
"seller_message": "Payment complete.",
"type": "authorized"
},
"paid": true,
"payment_intent": "pi_3JZDGj2sOmf47Nz90n2aOsuM",
"payment_method": "pm_1JZDGj2sOmf47Nz9S8jhZkFt",
"payment_intent": "pi_3Mlsrv2sOmf47Nz91hRe49rq",
"payment_method": "pm_1Mlsru2sOmf47Nz9s3VNHXYt",
"payment_method_details": {
"card": {
"brand": "visa",
@ -252,11 +260,12 @@ http_interactions:
},
"country": "US",
"exp_month": 4,
"exp_year": 2022,
"exp_year": 2024,
"fingerprint": "o52jybR7bnmNn6AT",
"funding": "credit",
"installments": null,
"last4": "4242",
"mandate": null,
"network": "visa",
"three_d_secure": null,
"wallet": null
@ -265,16 +274,14 @@ http_interactions:
},
"receipt_email": null,
"receipt_number": null,
"receipt_url": "https://pay.stripe.com/receipts/acct_103rE62sOmf47Nz9/ch_3JZDGj2sOmf47Nz90qQzwGqL/rcpt_KDeZMofbRKdRjLxxUw4LZO1LIDJP1sR",
"receipt_url": "https://pay.stripe.com/receipts/payment/CAcaFwoVYWNjdF8xMDNyRTYyc09tZjQ3Tno5KNTixqAGMgYuj2m72sQ6LBbKXya9pk9SHbdsAtPEkvHFS51bMvmjrLtHgTEs1uQ8YSkOpfxQRpKaC0PP",
"refunded": false,
"refunds": {
"object": "list",
"data": [
],
"data": [],
"has_more": false,
"total_count": 0,
"url": "/v1/charges/ch_3JZDGj2sOmf47Nz90qQzwGqL/refunds"
"url": "/v1/charges/ch_3Mlsrv2sOmf47Nz91760imzX/refunds"
},
"review": null,
"shipping": null,
@ -289,25 +296,26 @@ http_interactions:
],
"has_more": false,
"total_count": 1,
"url": "/v1/charges?payment_intent=pi_3JZDGj2sOmf47Nz90n2aOsuM"
"url": "/v1/charges?payment_intent=pi_3Mlsrv2sOmf47Nz91hRe49rq"
},
"client_secret": "pi_3JZDGj2sOmf47Nz90n2aOsuM_secret_WR5cdTATWgOShT8ZHInabONRL",
"client_secret": "pi_3Mlsrv2sOmf47Nz91hRe49rq_secret_gLvEqjCexLXGNWDLb7g2NpnfB",
"confirmation_method": "manual",
"created": 1631532253,
"created": 1678881107,
"currency": "usd",
"customer": "cus_8CzHcwBJtlA3IL",
"description": null,
"invoice": null,
"last_payment_error": null,
"latest_charge": "ch_3Mlsrv2sOmf47Nz91760imzX",
"livemode": false,
"metadata": {
},
"metadata": {},
"next_action": null,
"on_behalf_of": null,
"payment_method": "pm_1JZDGj2sOmf47Nz9S8jhZkFt",
"payment_method": "pm_1Mlsru2sOmf47Nz9s3VNHXYt",
"payment_method_options": {
"card": {
"installments": null,
"mandate_options": null,
"network": null,
"request_three_d_secure": "automatic"
}
@ -315,6 +323,7 @@ http_interactions:
"payment_method_types": [
"card"
],
"processing": null,
"receipt_email": null,
"review": null,
"setup_future_usage": null,
@ -326,13 +335,13 @@ http_interactions:
"transfer_data": null,
"transfer_group": null
}
recorded_at: Mon, 13 Sep 2021 11:24:15 GMT
recorded_at: Wed, 15 Mar 2023 11:51:48 GMT
- request:
method: post
uri: https://api.stripe.com/v1/payment_intents/pi_3JZDGj2sOmf47Nz90n2aOsuM
uri: https://api.stripe.com/v1/payment_intents/pi_3Mlsrv2sOmf47Nz91hRe49rq
body:
encoding: UTF-8
string: description=Invoice+reference%3A+2109001%2FVL
string: description=Invoice+reference%3A+2303007%2FVL
headers:
User-Agent:
- Stripe/v1 RubyBindings/5.29.0
@ -341,13 +350,13 @@ http_interactions:
Content-Type:
- application/x-www-form-urlencoded
X-Stripe-Client-Telemetry:
- '{"last_request_metrics":{"request_id":"req_EMDO1Z1Uux0kJb","request_duration_ms":1528}}'
- '{"last_request_metrics":{"request_id":"req_EocoIWBH6tucS4","request_duration_ms":1593}}'
Stripe-Version:
- '2019-08-14'
X-Stripe-Client-User-Agent:
- '{"bindings_version":"5.29.0","lang":"ruby","lang_version":"2.6.3 p62 (2019-04-16)","platform":"x86_64-darwin18","engine":"ruby","publisher":"stripe","uname":"Darwin
MacBook-Pro-Sleede-Peng 20.5.0 Darwin Kernel Version 20.5.0: Sat May 8 05:10:33
PDT 2021; root:xnu-7195.121.3~9/RELEASE_X86_64 x86_64","hostname":"MacBook-Pro-Sleede-Peng"}'
- '{"bindings_version":"5.29.0","lang":"ruby","lang_version":"2.6.10 p210 (2022-04-12)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux
version 6.2.5-arch1-1 (linux@archlinux) (gcc (GCC) 12.2.1 20230201, GNU ld
(GNU Binutils) 2.40) #1 SMP PREEMPT_DYNAMIC Sat, 11 Mar 2023 14:28:13 +0000","hostname":"Sylvain-desktop"}'
Accept-Encoding:
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept:
@ -360,11 +369,11 @@ http_interactions:
Server:
- nginx
Date:
- Mon, 13 Sep 2021 11:24:15 GMT
- Wed, 15 Mar 2023 11:51:49 GMT
Content-Type:
- application/json
Content-Length:
- '4286'
- '4544'
Connection:
- keep-alive
Access-Control-Allow-Credentials:
@ -379,25 +388,33 @@ http_interactions:
- '300'
Cache-Control:
- no-cache, no-store
Idempotency-Key:
- 75c78d8d-9df1-4bb7-af1b-7de3aeaa7056
Original-Request:
- req_MQd4Z7i8cW9FYF
Request-Id:
- req_Xmih0ndHQjzde4
- req_MQd4Z7i8cW9FYF
Stripe-Should-Retry:
- 'false'
Stripe-Version:
- '2019-08-14'
X-Stripe-C-Cost:
- '0'
Strict-Transport-Security:
- max-age=31556926; includeSubDomains; preload
- max-age=63072000; includeSubDomains; preload
body:
encoding: UTF-8
string: |
string: |-
{
"id": "pi_3JZDGj2sOmf47Nz90n2aOsuM",
"id": "pi_3Mlsrv2sOmf47Nz91hRe49rq",
"object": "payment_intent",
"amount": 1500,
"amount_capturable": 0,
"amount_details": {
"tip": {}
},
"amount_received": 1500,
"application": null,
"application_fee_amount": null,
"automatic_payment_methods": null,
"canceled_at": null,
"cancellation_reason": null,
"capture_method": "automatic",
@ -405,7 +422,7 @@ http_interactions:
"object": "list",
"data": [
{
"id": "ch_3JZDGj2sOmf47Nz90qQzwGqL",
"id": "ch_3Mlsrv2sOmf47Nz91760imzX",
"object": "charge",
"amount": 1500,
"amount_captured": 1500,
@ -413,7 +430,7 @@ http_interactions:
"application": null,
"application_fee": null,
"application_fee_amount": null,
"balance_transaction": "txn_3JZDGj2sOmf47Nz90fOwfgv5",
"balance_transaction": "txn_3Mlsrv2sOmf47Nz915VOM7IM",
"billing_details": {
"address": {
"city": null,
@ -429,34 +446,33 @@ http_interactions:
},
"calculated_statement_descriptor": "Stripe",
"captured": true,
"created": 1631532254,
"created": 1678881107,
"currency": "usd",
"customer": "cus_8CzHcwBJtlA3IL",
"description": null,
"destination": null,
"dispute": null,
"disputed": false,
"failure_balance_transaction": null,
"failure_code": null,
"failure_message": null,
"fraud_details": {
},
"fraud_details": {},
"invoice": null,
"livemode": false,
"metadata": {
},
"metadata": {},
"on_behalf_of": null,
"order": null,
"outcome": {
"network_status": "approved_by_network",
"reason": null,
"risk_level": "normal",
"risk_score": 22,
"risk_score": 7,
"seller_message": "Payment complete.",
"type": "authorized"
},
"paid": true,
"payment_intent": "pi_3JZDGj2sOmf47Nz90n2aOsuM",
"payment_method": "pm_1JZDGj2sOmf47Nz9S8jhZkFt",
"payment_intent": "pi_3Mlsrv2sOmf47Nz91hRe49rq",
"payment_method": "pm_1Mlsru2sOmf47Nz9s3VNHXYt",
"payment_method_details": {
"card": {
"brand": "visa",
@ -467,11 +483,12 @@ http_interactions:
},
"country": "US",
"exp_month": 4,
"exp_year": 2022,
"exp_year": 2024,
"fingerprint": "o52jybR7bnmNn6AT",
"funding": "credit",
"installments": null,
"last4": "4242",
"mandate": null,
"network": "visa",
"three_d_secure": null,
"wallet": null
@ -480,16 +497,14 @@ http_interactions:
},
"receipt_email": null,
"receipt_number": null,
"receipt_url": "https://pay.stripe.com/receipts/acct_103rE62sOmf47Nz9/ch_3JZDGj2sOmf47Nz90qQzwGqL/rcpt_KDeZMofbRKdRjLxxUw4LZO1LIDJP1sR",
"receipt_url": "https://pay.stripe.com/receipts/payment/CAcaFwoVYWNjdF8xMDNyRTYyc09tZjQ3Tno5KNXixqAGMgYPhCqQTfg6LBaYDHIkkSjReWHiH0dGP8uhn2RZUgWlfTBRya6YV380PXzkqIYppHOEGGj9",
"refunded": false,
"refunds": {
"object": "list",
"data": [
],
"data": [],
"has_more": false,
"total_count": 0,
"url": "/v1/charges/ch_3JZDGj2sOmf47Nz90qQzwGqL/refunds"
"url": "/v1/charges/ch_3Mlsrv2sOmf47Nz91760imzX/refunds"
},
"review": null,
"shipping": null,
@ -504,25 +519,26 @@ http_interactions:
],
"has_more": false,
"total_count": 1,
"url": "/v1/charges?payment_intent=pi_3JZDGj2sOmf47Nz90n2aOsuM"
"url": "/v1/charges?payment_intent=pi_3Mlsrv2sOmf47Nz91hRe49rq"
},
"client_secret": "pi_3JZDGj2sOmf47Nz90n2aOsuM_secret_WR5cdTATWgOShT8ZHInabONRL",
"client_secret": "pi_3Mlsrv2sOmf47Nz91hRe49rq_secret_gLvEqjCexLXGNWDLb7g2NpnfB",
"confirmation_method": "manual",
"created": 1631532253,
"created": 1678881107,
"currency": "usd",
"customer": "cus_8CzHcwBJtlA3IL",
"description": "Invoice reference: 2109001/VL",
"description": "Invoice reference: 2303007/VL",
"invoice": null,
"last_payment_error": null,
"latest_charge": "ch_3Mlsrv2sOmf47Nz91760imzX",
"livemode": false,
"metadata": {
},
"metadata": {},
"next_action": null,
"on_behalf_of": null,
"payment_method": "pm_1JZDGj2sOmf47Nz9S8jhZkFt",
"payment_method": "pm_1Mlsru2sOmf47Nz9s3VNHXYt",
"payment_method_options": {
"card": {
"installments": null,
"mandate_options": null,
"network": null,
"request_three_d_secure": "automatic"
}
@ -530,6 +546,7 @@ http_interactions:
"payment_method_types": [
"card"
],
"processing": null,
"receipt_email": null,
"review": null,
"setup_future_usage": null,
@ -541,5 +558,5 @@ http_interactions:
"transfer_data": null,
"transfer_group": null
}
recorded_at: Mon, 13 Sep 2021 11:24:15 GMT
recorded_at: Wed, 15 Mar 2023 11:51:49 GMT
recorded_with: VCR 6.0.0