@driveflux/api-functions 1.0.54 → 1.0.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/confirm.js +24 -29
- package/dist/auth/emails.js +12 -13
- package/dist/auth/formatter.js +5 -5
- package/dist/auth/otp.js +66 -50
- package/dist/auth/register.js +42 -34
- package/dist/auth/tokens.js +58 -55
- package/dist/auth/verifications.js +53 -52
- package/dist/constants.js +0 -1
- package/dist/create-logger.js +1 -2
- package/dist/mailjet/calls/manage-contacts-in-list.js +5 -6
- package/dist/mailjet/calls/manage-subscription-status.js +4 -5
- package/dist/mailjet/calls/request-service.js +7 -6
- package/dist/mailjet/refresh-email-preferences.js +11 -12
- package/dist/mailjet/set-contact.js +11 -12
- package/dist/mailjet/types.js +1 -2
- package/dist/mailjet/utils/convert-to-array.js +8 -6
- package/dist/mailjet/utils/extract-email-preferences.js +14 -15
- package/dist/mailjet/utils/lists.js +7 -8
- package/dist/mailjet/utils/update-email-references.js +16 -15
- package/dist/notion/client.js +22 -19
- package/dist/notion/helpful.js +6 -9
- package/dist/notion/schemas/block.js +42 -48
- package/dist/notion/schemas/common.js +9 -14
- package/dist/notion/schemas/database.js +62 -60
- package/dist/notion/schemas/emoji.js +1 -2
- package/dist/notion/schemas/file.js +9 -9
- package/dist/notion/schemas/kb.js +5 -6
- package/dist/notion/schemas/page.js +72 -61
- package/dist/notion/schemas/parent.js +4 -5
- package/dist/notion/schemas/user.js +18 -19
- package/dist/reservation/agree.js +6 -7
- package/dist/reservation/checks.js +3 -4
- package/dist/reservation/display-vehicle.js +65 -71
- package/dist/reservation/ensure-user-billing-address.js +9 -11
- package/dist/reservation/fetch-or-create.js +48 -54
- package/dist/reservation/invoice.js +70 -78
- package/dist/reservation/payer.js +5 -6
- package/dist/reservation/payment-intent-sync.js +4 -6
- package/dist/reservation/reserve.js +3 -4
- package/dist/reservation/types.js +1 -2
- package/dist/reservation/vehicle.js +13 -16
- package/dist/slack.js +24 -29
- package/dist/validation.js +77 -79
- package/dist/vehicle/vehicle-pricing/constants.js +22 -19
- package/dist/vehicle/vehicle-pricing/index.js +28 -42
- package/dist/vehicle/vehicle-pricing/types.js +1 -2
- package/package.json +9 -9
|
@@ -3,29 +3,27 @@ import { makeProblem, PROBLEM_INTERNAL } from '@driveflux/problem';
|
|
|
3
3
|
import { Err, Ok } from '@driveflux/result';
|
|
4
4
|
import { createScopedLogger } from '../create-logger.js';
|
|
5
5
|
const log = createScopedLogger('reservation:ensure-user-billing-address');
|
|
6
|
-
export const ensureUserBillingAddress = async (userOrBusiness, deliveryAddresses)
|
|
6
|
+
export const ensureUserBillingAddress = async (userOrBusiness, deliveryAddresses)=>{
|
|
7
7
|
if (userOrBusiness.addresses?.billing) {
|
|
8
8
|
return new Ok(userOrBusiness);
|
|
9
9
|
}
|
|
10
10
|
try {
|
|
11
11
|
const fullQuery = {
|
|
12
|
-
where: {
|
|
12
|
+
where: {
|
|
13
|
+
id: userOrBusiness.id
|
|
14
|
+
},
|
|
13
15
|
data: {
|
|
14
16
|
addresses: {
|
|
15
17
|
home: deliveryAddresses.home,
|
|
16
|
-
billing: deliveryAddresses.billing
|
|
17
|
-
}
|
|
18
|
-
}
|
|
18
|
+
billing: deliveryAddresses.billing
|
|
19
|
+
}
|
|
20
|
+
}
|
|
19
21
|
};
|
|
20
22
|
// Use the delivery addresses
|
|
21
|
-
const updatedUserOrBusiness = userOrBusiness.object === 'user'
|
|
22
|
-
? await prisma.user.update(fullQuery)
|
|
23
|
-
: await prisma.business.update(fullQuery);
|
|
23
|
+
const updatedUserOrBusiness = userOrBusiness.object === 'user' ? await prisma.user.update(fullQuery) : await prisma.business.update(fullQuery);
|
|
24
24
|
return new Ok(updatedUserOrBusiness);
|
|
25
|
-
}
|
|
26
|
-
catch (error) {
|
|
25
|
+
} catch (error) {
|
|
27
26
|
log.error(error, 'Failed to ensure user billing address');
|
|
28
27
|
return new Err(makeProblem(PROBLEM_INTERNAL, 'Failed to ensure user billing address'));
|
|
29
28
|
}
|
|
30
29
|
};
|
|
31
|
-
//# sourceMappingURL=ensure-user-billing-address.js.map
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { prisma
|
|
1
|
+
import { prisma } from '@driveflux/db';
|
|
2
2
|
import { generateId } from '@driveflux/db/id';
|
|
3
3
|
import { reportError } from '@driveflux/reporter';
|
|
4
4
|
import { Ok } from '@driveflux/result';
|
|
5
5
|
import { slackLater } from '../slack.js';
|
|
6
|
-
import { createReservationInvoice, updateReservationInvoiceIfNeeded
|
|
7
|
-
export const fetchOrCreateReservation = async ({ vehicle, payer, body
|
|
8
|
-
const { subscribingUser, plan, mileagePackage, couponCode, referralCode, asBusiness, source, deliveryAddresses, deliveryDate, analytics, paymentIntentId, freeReservation, freeReservationReason
|
|
6
|
+
import { createReservationInvoice, updateReservationInvoiceIfNeeded } from './invoice.js';
|
|
7
|
+
export const fetchOrCreateReservation = async ({ vehicle, payer, body })=>{
|
|
8
|
+
const { subscribingUser, plan, mileagePackage, couponCode, referralCode, asBusiness, source, deliveryAddresses, deliveryDate, analytics, paymentIntentId, freeReservation, freeReservationReason } = body;
|
|
9
9
|
try {
|
|
10
10
|
const previousReservation = await prisma.subscriptionReservation.findFirst({
|
|
11
11
|
where: {
|
|
@@ -19,14 +19,14 @@ export const fetchOrCreateReservation = async ({ vehicle, payer, body, }) => {
|
|
|
19
19
|
source,
|
|
20
20
|
// If the subscription ID was populated, this means that this reservation was consumed into a subsription,
|
|
21
21
|
// so we shouldn't re-use it
|
|
22
|
-
subscription: null
|
|
22
|
+
subscription: null
|
|
23
23
|
},
|
|
24
24
|
orderBy: {
|
|
25
|
-
updatedAt: 'desc'
|
|
25
|
+
updatedAt: 'desc'
|
|
26
26
|
},
|
|
27
27
|
include: {
|
|
28
|
-
invoice: true
|
|
29
|
-
}
|
|
28
|
+
invoice: true
|
|
29
|
+
}
|
|
30
30
|
});
|
|
31
31
|
if (previousReservation) {
|
|
32
32
|
if (previousReservation.invoice.voided) {
|
|
@@ -39,7 +39,7 @@ export const fetchOrCreateReservation = async ({ vehicle, payer, body, }) => {
|
|
|
39
39
|
'partiallyPaid',
|
|
40
40
|
'partiallyRefunded',
|
|
41
41
|
'refunded',
|
|
42
|
-
'pendingRefund'
|
|
42
|
+
'pendingRefund'
|
|
43
43
|
];
|
|
44
44
|
if (wrongStatuses.includes(previousReservation.invoice.status)) {
|
|
45
45
|
const updatedReservation = await recreateReservationInvoiceAndUpdateReservation(previousReservation, body, vehicle, payer);
|
|
@@ -52,16 +52,16 @@ export const fetchOrCreateReservation = async ({ vehicle, payer, body, }) => {
|
|
|
52
52
|
text: {
|
|
53
53
|
type: 'plain_text',
|
|
54
54
|
text: '‼️ Reservation invoice in wrong status',
|
|
55
|
-
emoji: true
|
|
56
|
-
}
|
|
55
|
+
emoji: true
|
|
56
|
+
}
|
|
57
57
|
},
|
|
58
58
|
{
|
|
59
59
|
type: 'section',
|
|
60
60
|
text: {
|
|
61
61
|
type: 'plain_text',
|
|
62
|
-
text: `The reservation invoice ${previousReservation.invoice.id} was in a wrong status (${previousReservation.invoice.status}), it was discarded and a new invoice (${updatedReservation.val.id}) was created, however, investigate the issue
|
|
63
|
-
}
|
|
64
|
-
}
|
|
62
|
+
text: `The reservation invoice ${previousReservation.invoice.id} was in a wrong status (${previousReservation.invoice.status}), it was discarded and a new invoice (${updatedReservation.val.id}) was created, however, investigate the issue.`
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
65
|
]);
|
|
66
66
|
return updatedReservation;
|
|
67
67
|
}
|
|
@@ -73,8 +73,7 @@ export const fetchOrCreateReservation = async ({ vehicle, payer, body, }) => {
|
|
|
73
73
|
// Otherwise, we need to create a new reservation
|
|
74
74
|
return new Ok(previousReservation);
|
|
75
75
|
}
|
|
76
|
-
}
|
|
77
|
-
catch (error) {
|
|
76
|
+
} catch (error) {
|
|
78
77
|
// We couldn't fetch the reservation, so we create a new one and just log this
|
|
79
78
|
await reportError(error);
|
|
80
79
|
}
|
|
@@ -93,7 +92,7 @@ export const fetchOrCreateReservation = async ({ vehicle, payer, body, }) => {
|
|
|
93
92
|
analytics,
|
|
94
93
|
vehicle,
|
|
95
94
|
payer,
|
|
96
|
-
subscribingUser
|
|
95
|
+
subscribingUser
|
|
97
96
|
});
|
|
98
97
|
if (invoiceResult.err) {
|
|
99
98
|
return invoiceResult;
|
|
@@ -112,70 +111,66 @@ export const fetchOrCreateReservation = async ({ vehicle, payer, body, }) => {
|
|
|
112
111
|
deliveryAddresses,
|
|
113
112
|
deliveryDate,
|
|
114
113
|
asBusiness,
|
|
115
|
-
source
|
|
114
|
+
source
|
|
116
115
|
});
|
|
117
116
|
return new Ok(reservation);
|
|
118
117
|
};
|
|
119
|
-
const createReservation = async ({ reservationId, invoiceId, subscribingUserId, vehicleId, businessId, plan, mileagePackage, couponCode, referralCode, analytics, deliveryAddresses, deliveryDate, asBusiness, source
|
|
118
|
+
const createReservation = async ({ reservationId, invoiceId, subscribingUserId, vehicleId, businessId, plan, mileagePackage, couponCode, referralCode, analytics, deliveryAddresses, deliveryDate, asBusiness, source })=>{
|
|
120
119
|
return await prisma.subscriptionReservation.create({
|
|
121
120
|
data: {
|
|
122
121
|
id: reservationId,
|
|
123
122
|
invoice: {
|
|
124
123
|
connect: {
|
|
125
|
-
id: invoiceId
|
|
126
|
-
}
|
|
124
|
+
id: invoiceId
|
|
125
|
+
}
|
|
127
126
|
},
|
|
128
127
|
user: {
|
|
129
128
|
connect: {
|
|
130
|
-
id: subscribingUserId
|
|
131
|
-
}
|
|
129
|
+
id: subscribingUserId
|
|
130
|
+
}
|
|
132
131
|
},
|
|
133
132
|
vehicle: {
|
|
134
133
|
connect: {
|
|
135
|
-
id: vehicleId
|
|
136
|
-
}
|
|
134
|
+
id: vehicleId
|
|
135
|
+
}
|
|
137
136
|
},
|
|
138
|
-
...
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
},
|
|
144
|
-
},
|
|
137
|
+
...businessId ? {
|
|
138
|
+
business: {
|
|
139
|
+
connect: {
|
|
140
|
+
id: businessId
|
|
141
|
+
}
|
|
145
142
|
}
|
|
146
|
-
|
|
143
|
+
} : undefined,
|
|
147
144
|
plan,
|
|
148
145
|
mileagePackage,
|
|
149
146
|
couponCode: couponCode,
|
|
150
147
|
deliveryAddresses: deliveryAddresses,
|
|
151
148
|
deliveryDate: deliveryDate,
|
|
152
|
-
...
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
},
|
|
158
|
-
},
|
|
149
|
+
...referralCode ? {
|
|
150
|
+
referral: {
|
|
151
|
+
connect: {
|
|
152
|
+
id: referralCode
|
|
153
|
+
}
|
|
159
154
|
}
|
|
160
|
-
|
|
155
|
+
} : undefined,
|
|
161
156
|
asBusiness: !!asBusiness,
|
|
162
157
|
source,
|
|
163
158
|
metadata: {
|
|
164
|
-
analytics
|
|
165
|
-
}
|
|
159
|
+
analytics
|
|
160
|
+
}
|
|
166
161
|
},
|
|
167
162
|
include: {
|
|
168
|
-
invoice: true
|
|
169
|
-
}
|
|
163
|
+
invoice: true
|
|
164
|
+
}
|
|
170
165
|
});
|
|
171
166
|
};
|
|
172
|
-
const recreateReservationInvoiceAndUpdateReservation = async (previousReservation, body, vehicle, payer)
|
|
167
|
+
const recreateReservationInvoiceAndUpdateReservation = async (previousReservation, body, vehicle, payer)=>{
|
|
173
168
|
const invoiceResult = await createReservationInvoice({
|
|
174
169
|
...body,
|
|
175
170
|
invoiceId: generateId('Invoice'),
|
|
176
171
|
reservationId: previousReservation.id,
|
|
177
172
|
vehicle,
|
|
178
|
-
payer
|
|
173
|
+
payer
|
|
179
174
|
});
|
|
180
175
|
if (!invoiceResult.ok) {
|
|
181
176
|
return invoiceResult;
|
|
@@ -183,19 +178,18 @@ const recreateReservationInvoiceAndUpdateReservation = async (previousReservatio
|
|
|
183
178
|
const invoice = invoiceResult.val;
|
|
184
179
|
const updatedReservation = await prisma.subscriptionReservation.update({
|
|
185
180
|
where: {
|
|
186
|
-
id: previousReservation.id
|
|
181
|
+
id: previousReservation.id
|
|
187
182
|
},
|
|
188
183
|
data: {
|
|
189
184
|
invoice: {
|
|
190
185
|
connect: {
|
|
191
|
-
id: invoice.id
|
|
192
|
-
}
|
|
193
|
-
}
|
|
186
|
+
id: invoice.id
|
|
187
|
+
}
|
|
188
|
+
}
|
|
194
189
|
},
|
|
195
190
|
include: {
|
|
196
|
-
invoice: true
|
|
197
|
-
}
|
|
191
|
+
invoice: true
|
|
192
|
+
}
|
|
198
193
|
});
|
|
199
194
|
return new Ok(updatedReservation);
|
|
200
195
|
};
|
|
201
|
-
//# sourceMappingURL=fetch-or-create.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { config } from '@driveflux/config/backend';
|
|
2
2
|
import { loadCoupon, PROBLEM_APPLICABLE_NOT_FOUND } from '@driveflux/coupon';
|
|
3
|
-
import { prisma
|
|
3
|
+
import { prisma } from '@driveflux/db';
|
|
4
4
|
import { generateId } from '@driveflux/db/id';
|
|
5
5
|
import { EMPTY_BILLING_ADDRESS } from '@driveflux/db/models/other';
|
|
6
6
|
import { PURPOSE_RESERVATION } from '@driveflux/db/models/subscription';
|
|
@@ -14,16 +14,15 @@ import { isAfter } from 'date-fns/isAfter';
|
|
|
14
14
|
import { subMinutes } from 'date-fns/subMinutes';
|
|
15
15
|
import { createScopedLogger } from '../create-logger.js';
|
|
16
16
|
const log = createScopedLogger('reservation:invoice');
|
|
17
|
-
export const updateReservationInvoiceIfNeeded = async (oldReservationInvoice, newParams)
|
|
17
|
+
export const updateReservationInvoiceIfNeeded = async (oldReservationInvoice, newParams)=>{
|
|
18
18
|
const couponIsDifferent = oldReservationInvoice.couponCode !== newParams.couponCode;
|
|
19
|
-
if (!couponIsDifferent &&
|
|
20
|
-
oldReservationInvoice.stripePaymentIntentId === newParams.paymentIntentId) {
|
|
19
|
+
if (!couponIsDifferent && oldReservationInvoice.stripePaymentIntentId === newParams.paymentIntentId) {
|
|
21
20
|
return new Ok(oldReservationInvoice);
|
|
22
21
|
}
|
|
23
22
|
// If the coupon or payment intent are differnt we should update the invoice
|
|
24
23
|
let update = {
|
|
25
24
|
stripePaymentIntentId: newParams.paymentIntentId,
|
|
26
|
-
couponCode: newParams.couponCode
|
|
25
|
+
couponCode: newParams.couponCode
|
|
27
26
|
};
|
|
28
27
|
// If the coupon is different, we need to apply the coupon to the invoice
|
|
29
28
|
if (couponIsDifferent) {
|
|
@@ -31,63 +30,56 @@ export const updateReservationInvoiceIfNeeded = async (oldReservationInvoice, ne
|
|
|
31
30
|
const result = await coupons.applyCouponToUnsavedInvoice(newParams.couponCode, oldReservationInvoice);
|
|
32
31
|
if (result.ok) {
|
|
33
32
|
const { discounts, ...rest } = result.val;
|
|
34
|
-
const toDisconnect = discounts
|
|
35
|
-
|
|
36
|
-
:
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
: undefined;
|
|
33
|
+
const toDisconnect = discounts ? oldReservationInvoice.discountIds?.filter((id)=>!discounts.connect?.some((d)=>d.id === id)) : undefined;
|
|
34
|
+
const disconnectClauses = toDisconnect?.length ? {
|
|
35
|
+
disconnect: toDisconnect.map((id)=>({
|
|
36
|
+
id
|
|
37
|
+
}))
|
|
38
|
+
} : undefined;
|
|
42
39
|
update = {
|
|
43
40
|
...update,
|
|
44
41
|
...rest,
|
|
45
|
-
...
|
|
46
|
-
|
|
47
|
-
discounts
|
|
48
|
-
|
|
49
|
-
...(disconnectClauses || {}),
|
|
50
|
-
},
|
|
42
|
+
...discounts || disconnectClauses ? {
|
|
43
|
+
discounts: {
|
|
44
|
+
...discounts || {},
|
|
45
|
+
...disconnectClauses || {}
|
|
51
46
|
}
|
|
52
|
-
|
|
47
|
+
} : {},
|
|
53
48
|
metadata: {
|
|
54
49
|
...update.metadata,
|
|
55
|
-
couponCode: newParams.couponCode
|
|
50
|
+
couponCode: newParams.couponCode
|
|
56
51
|
},
|
|
57
52
|
providerMetadata: {
|
|
58
53
|
...update.providerMetadata,
|
|
59
|
-
couponCode: newParams.couponCode
|
|
60
|
-
}
|
|
54
|
+
couponCode: newParams.couponCode
|
|
55
|
+
}
|
|
61
56
|
};
|
|
62
57
|
}
|
|
63
58
|
if (result.err && result.val.code !== PROBLEM_APPLICABLE_NOT_FOUND) {
|
|
64
59
|
return result;
|
|
65
60
|
}
|
|
66
|
-
}
|
|
67
|
-
else if (oldReservationInvoice.discountIds.length) {
|
|
61
|
+
} else if (oldReservationInvoice.discountIds.length) {
|
|
68
62
|
// No discounts, so we disconnect all of them
|
|
69
63
|
update = {
|
|
70
64
|
discounts: {
|
|
71
|
-
disconnect: oldReservationInvoice.discountIds.map((id)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
65
|
+
disconnect: oldReservationInvoice.discountIds.map((id)=>({
|
|
66
|
+
id
|
|
67
|
+
}))
|
|
68
|
+
}
|
|
75
69
|
};
|
|
76
70
|
}
|
|
77
71
|
}
|
|
78
72
|
let updated = await wrapInResult(biller.updateInvoice(oldReservationInvoice.id, update));
|
|
79
73
|
// If the invoice was locked due to pending status and we're more than 1 minute old, we unlock it
|
|
80
74
|
if (updated.err && updated.val.code === 'invoice_pending') {
|
|
81
|
-
if ((oldReservationInvoice.lockedAt
|
|
82
|
-
isAfter(oldReservationInvoice.lockedAt, subMinutes(new Date(), 1))) ||
|
|
83
|
-
!oldReservationInvoice.lockedAt) {
|
|
75
|
+
if (oldReservationInvoice.lockedAt && isAfter(oldReservationInvoice.lockedAt, subMinutes(new Date(), 1)) || !oldReservationInvoice.lockedAt) {
|
|
84
76
|
updated = await wrapInResult(biller.updateInvoice(oldReservationInvoice.id, {
|
|
85
77
|
...update,
|
|
86
78
|
locked: false,
|
|
87
79
|
lockedAt: null,
|
|
88
80
|
lockReason: null,
|
|
89
81
|
unlockAt: null,
|
|
90
|
-
status: 'draft'
|
|
82
|
+
status: 'draft'
|
|
91
83
|
}));
|
|
92
84
|
}
|
|
93
85
|
}
|
|
@@ -96,7 +88,7 @@ export const updateReservationInvoiceIfNeeded = async (oldReservationInvoice, ne
|
|
|
96
88
|
}
|
|
97
89
|
return new Ok(updated.val);
|
|
98
90
|
};
|
|
99
|
-
export const createReservationInvoice = async ({ invoiceId, couponCode, plan, mileagePackage, referralCode, reservationId, paymentIntentId, freeReservation, freeReservationReason, analytics, vehicle, payer, subscribingUser
|
|
91
|
+
export const createReservationInvoice = async ({ invoiceId, couponCode, plan, mileagePackage, referralCode, reservationId, paymentIntentId, freeReservation, freeReservationReason, analytics, vehicle, payer, subscribingUser })=>{
|
|
100
92
|
if (freeReservation) {
|
|
101
93
|
// It's a free reservation, so we don't need to add a coupon code
|
|
102
94
|
return await wrapInResult(biller.createInvoice(getInvoiceCreateDetails({
|
|
@@ -109,21 +101,17 @@ export const createReservationInvoice = async ({ invoiceId, couponCode, plan, mi
|
|
|
109
101
|
freeReservation,
|
|
110
102
|
freeReservationReason,
|
|
111
103
|
analytics,
|
|
112
|
-
reservationId
|
|
104
|
+
reservationId
|
|
113
105
|
})));
|
|
114
106
|
}
|
|
115
107
|
let finalCouponCode = couponCode;
|
|
116
108
|
// If we don't have a reservation coupon ID, we apply the referral discount if any
|
|
117
|
-
const referral = referralCode &&
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
if (!finalCouponCode &&
|
|
124
|
-
referral &&
|
|
125
|
-
referral.discountToReceiverCouponId &&
|
|
126
|
-
referral.discountToReceiverCouponApplicationName === 'reservationFee') {
|
|
109
|
+
const referral = referralCode && await prisma.referral.findUnique({
|
|
110
|
+
where: {
|
|
111
|
+
id: referralCode
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
if (!finalCouponCode && referral && referral.discountToReceiverCouponId && referral.discountToReceiverCouponApplicationName === 'reservationFee') {
|
|
127
115
|
const referralCoupon = await loadCoupon(referral.discountToReceiverCouponId);
|
|
128
116
|
if (referralCoupon) {
|
|
129
117
|
finalCouponCode = referralCoupon.code;
|
|
@@ -142,18 +130,20 @@ export const createReservationInvoice = async ({ invoiceId, couponCode, plan, mi
|
|
|
142
130
|
freeReservation,
|
|
143
131
|
freeReservationReason,
|
|
144
132
|
analytics,
|
|
145
|
-
reservationId
|
|
133
|
+
reservationId
|
|
146
134
|
}),
|
|
147
135
|
lines: [
|
|
148
136
|
{
|
|
149
137
|
amount: reservationAmount,
|
|
150
138
|
description: `Reservation fee for ${vehicleName(vehicle)}`,
|
|
151
|
-
chargingFor: 'reservationFee'
|
|
152
|
-
}
|
|
153
|
-
]
|
|
139
|
+
chargingFor: 'reservationFee'
|
|
140
|
+
}
|
|
141
|
+
]
|
|
154
142
|
};
|
|
155
143
|
if (finalCouponCode) {
|
|
156
|
-
log.debug({
|
|
144
|
+
log.debug({
|
|
145
|
+
finalCouponCode
|
|
146
|
+
}, 'Applying coupon code to invoice');
|
|
157
147
|
const withCoupon = await coupons.applyCouponToUnsavedInvoice(finalCouponCode, invoiceCreateDetails, 'reservationFee', {
|
|
158
148
|
subscriptionPlans: plan,
|
|
159
149
|
subscriptionMileagePackages: mileagePackage,
|
|
@@ -161,24 +151,25 @@ export const createReservationInvoice = async ({ invoiceId, couponCode, plan, mi
|
|
|
161
151
|
vehicleMake: vehicle.make,
|
|
162
152
|
userId: subscribingUser.id,
|
|
163
153
|
businessId: payer.object === 'business' ? payer.id : undefined,
|
|
164
|
-
vehicleType: vehicle.type
|
|
154
|
+
vehicleType: vehicle.type
|
|
165
155
|
});
|
|
166
|
-
if (withCoupon.err &&
|
|
167
|
-
withCoupon.val.code !== PROBLEM_APPLICABLE_NOT_FOUND) {
|
|
156
|
+
if (withCoupon.err && withCoupon.val.code !== PROBLEM_APPLICABLE_NOT_FOUND) {
|
|
168
157
|
return withCoupon;
|
|
169
158
|
}
|
|
170
|
-
log.debug({
|
|
159
|
+
log.debug({
|
|
160
|
+
couponUpdates: withCoupon.val
|
|
161
|
+
}, 'Coupon applied to invoice');
|
|
171
162
|
if (withCoupon.ok) {
|
|
172
163
|
invoiceCreateDetails = {
|
|
173
164
|
...withCoupon.val,
|
|
174
165
|
metadata: {
|
|
175
166
|
...withCoupon.val.metadata,
|
|
176
|
-
couponCode: finalCouponCode
|
|
167
|
+
couponCode: finalCouponCode
|
|
177
168
|
},
|
|
178
169
|
providerMetadata: {
|
|
179
170
|
...withCoupon.val.providerMetadata,
|
|
180
|
-
couponCode: finalCouponCode
|
|
181
|
-
}
|
|
171
|
+
couponCode: finalCouponCode
|
|
172
|
+
}
|
|
182
173
|
};
|
|
183
174
|
}
|
|
184
175
|
}
|
|
@@ -186,15 +177,15 @@ export const createReservationInvoice = async ({ invoiceId, couponCode, plan, mi
|
|
|
186
177
|
...invoiceCreateDetails,
|
|
187
178
|
providerMetadata: {
|
|
188
179
|
...invoiceCreateDetails.providerMetadata,
|
|
189
|
-
invoiceId: invoiceCreateDetails.id
|
|
190
|
-
}
|
|
180
|
+
invoiceId: invoiceCreateDetails.id
|
|
181
|
+
}
|
|
191
182
|
}));
|
|
192
183
|
if (result.err) {
|
|
193
184
|
return result;
|
|
194
185
|
}
|
|
195
186
|
return new Ok(result.val);
|
|
196
187
|
};
|
|
197
|
-
const getInvoiceCreateDetails = ({ invoiceId, payer, date, reservationDiscountId, vehicle, plan, mileagePackage, couponCode, paymentIntentId, freeReservation, freeReservationReason, analytics, reservationId
|
|
188
|
+
const getInvoiceCreateDetails = ({ invoiceId, payer, date, reservationDiscountId, vehicle, plan, mileagePackage, couponCode, paymentIntentId, freeReservation, freeReservationReason, analytics, reservationId })=>{
|
|
198
189
|
return {
|
|
199
190
|
id: invoiceId || generateId('Invoice'),
|
|
200
191
|
taxPercentage: 0,
|
|
@@ -210,15 +201,13 @@ const getInvoiceCreateDetails = ({ invoiceId, payer, date, reservationDiscountId
|
|
|
210
201
|
description: 'Reservation fee for a FLUX subscription',
|
|
211
202
|
footer: `Invoice for a FLUX subscription reservation. Due by ${format(date ?? new Date())}`,
|
|
212
203
|
invoiceUrl: `${config.appUrl}/invoice-preview/${invoiceId}`,
|
|
213
|
-
...
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
},
|
|
219
|
-
},
|
|
204
|
+
...reservationDiscountId ? {
|
|
205
|
+
discounts: {
|
|
206
|
+
connect: {
|
|
207
|
+
id: reservationDiscountId
|
|
208
|
+
}
|
|
220
209
|
}
|
|
221
|
-
|
|
210
|
+
} : {},
|
|
222
211
|
lines: [],
|
|
223
212
|
subscriptionDetails: {
|
|
224
213
|
vehicleId: vehicle.id,
|
|
@@ -227,7 +216,7 @@ const getInvoiceCreateDetails = ({ invoiceId, payer, date, reservationDiscountId
|
|
|
227
216
|
variant: vehicle.variant,
|
|
228
217
|
year: vehicle.year,
|
|
229
218
|
plan: plan,
|
|
230
|
-
mileagePackage: mileagePackage
|
|
219
|
+
mileagePackage: mileagePackage
|
|
231
220
|
},
|
|
232
221
|
// Legacy field:
|
|
233
222
|
stripePaymentIntentId: paymentIntentId,
|
|
@@ -237,14 +226,18 @@ const getInvoiceCreateDetails = ({ invoiceId, payer, date, reservationDiscountId
|
|
|
237
226
|
mileagePackage,
|
|
238
227
|
vehicleId: vehicle.id,
|
|
239
228
|
vehicleName: vehicleName(vehicle),
|
|
240
|
-
...
|
|
241
|
-
|
|
229
|
+
...reservationId ? {
|
|
230
|
+
reservationId
|
|
231
|
+
} : {},
|
|
232
|
+
...couponCode ? {
|
|
233
|
+
couponCode
|
|
234
|
+
} : {}
|
|
242
235
|
},
|
|
243
236
|
autoRetry: {
|
|
244
237
|
enabled: false,
|
|
245
238
|
interval: 0,
|
|
246
239
|
maxAttempts: 0,
|
|
247
|
-
attempts: 0
|
|
240
|
+
attempts: 0
|
|
248
241
|
},
|
|
249
242
|
metadata: {
|
|
250
243
|
purpose: PURPOSE_RESERVATION,
|
|
@@ -253,11 +246,10 @@ const getInvoiceCreateDetails = ({ invoiceId, payer, date, reservationDiscountId
|
|
|
253
246
|
plan,
|
|
254
247
|
mileagePackage,
|
|
255
248
|
couponCode,
|
|
256
|
-
...
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
analytics
|
|
260
|
-
}
|
|
249
|
+
...freeReservation && freeReservationReason ? {
|
|
250
|
+
freeReservationReason
|
|
251
|
+
} : {},
|
|
252
|
+
analytics
|
|
253
|
+
}
|
|
261
254
|
};
|
|
262
255
|
};
|
|
263
|
-
//# sourceMappingURL=invoice.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { prisma } from '@driveflux/db';
|
|
2
|
-
import { makeProblem, PROBLEM_CONDITION_FAILED, PROBLEM_NOT_FOUND
|
|
2
|
+
import { makeProblem, PROBLEM_CONDITION_FAILED, PROBLEM_NOT_FOUND } from '@driveflux/problem';
|
|
3
3
|
import { Err, Ok } from '@driveflux/result';
|
|
4
|
-
export const getPayer = async ({ subscribingUser, asBusiness
|
|
4
|
+
export const getPayer = async ({ subscribingUser, asBusiness })=>{
|
|
5
5
|
let business = null;
|
|
6
6
|
let payer = subscribingUser;
|
|
7
7
|
if (asBusiness) {
|
|
@@ -11,8 +11,8 @@ export const getPayer = async ({ subscribingUser, asBusiness, }) => {
|
|
|
11
11
|
// load business
|
|
12
12
|
business = await prisma.business.findUnique({
|
|
13
13
|
where: {
|
|
14
|
-
id: subscribingUser.businessId
|
|
15
|
-
}
|
|
14
|
+
id: subscribingUser.businessId
|
|
15
|
+
}
|
|
16
16
|
});
|
|
17
17
|
if (!business) {
|
|
18
18
|
return new Err(makeProblem(PROBLEM_NOT_FOUND, `The business ${subscribingUser.businessId} was not found.`));
|
|
@@ -26,7 +26,7 @@ export const getPayer = async ({ subscribingUser, asBusiness, }) => {
|
|
|
26
26
|
}
|
|
27
27
|
return new Ok(payer);
|
|
28
28
|
};
|
|
29
|
-
const checkPayerObjectSanity = (subscriber)
|
|
29
|
+
const checkPayerObjectSanity = (subscriber)=>{
|
|
30
30
|
if (!subscriber.email) {
|
|
31
31
|
return new Err(makeProblem(PROBLEM_CONDITION_FAILED, `Subscriber ${subscriber.id} has no email. Please add a verified email first.`));
|
|
32
32
|
}
|
|
@@ -35,4 +35,3 @@ const checkPayerObjectSanity = (subscriber) => {
|
|
|
35
35
|
}
|
|
36
36
|
return new Ok(true);
|
|
37
37
|
};
|
|
38
|
-
//# sourceMappingURL=payer.js.map
|
|
@@ -3,20 +3,18 @@ import { makeProblem, PROBLEM_EXTERNAL } from '@driveflux/problem';
|
|
|
3
3
|
import { Err, Ok } from '@driveflux/result';
|
|
4
4
|
import { createScopedLogger } from '../create-logger.js';
|
|
5
5
|
const log = createScopedLogger('reservation:payment-intent-sync');
|
|
6
|
-
export const ensurePaymentIntentIdIsSynced = async (invoice, paymentIntentId)
|
|
6
|
+
export const ensurePaymentIntentIdIsSynced = async (invoice, paymentIntentId)=>{
|
|
7
7
|
const stripe = biller.getPaymentProvider('stripe').getStripe();
|
|
8
8
|
try {
|
|
9
9
|
await stripe.paymentIntents.update(paymentIntentId, {
|
|
10
10
|
metadata: {
|
|
11
11
|
...invoice.providerMetadata,
|
|
12
|
-
invoiceId: invoice.id
|
|
13
|
-
}
|
|
12
|
+
invoiceId: invoice.id
|
|
13
|
+
}
|
|
14
14
|
});
|
|
15
15
|
return new Ok(true);
|
|
16
|
-
}
|
|
17
|
-
catch (error) {
|
|
16
|
+
} catch (error) {
|
|
18
17
|
log.error(error, 'Error updating payment intent metadata');
|
|
19
18
|
return new Err(makeProblem(PROBLEM_EXTERNAL, 'Error updating payment intent metadata'));
|
|
20
19
|
}
|
|
21
20
|
};
|
|
22
|
-
//# sourceMappingURL=payment-intent-sync.js.map
|
|
@@ -7,14 +7,14 @@ import { fetchOrCreateReservation } from './fetch-or-create.js';
|
|
|
7
7
|
import { getPayer } from './payer.js';
|
|
8
8
|
import { ensurePaymentIntentIdIsSynced } from './payment-intent-sync.js';
|
|
9
9
|
import { getVehicle } from './vehicle.js';
|
|
10
|
-
const processBody = (body)
|
|
10
|
+
const processBody = (body)=>{
|
|
11
11
|
// Switch mileage package
|
|
12
12
|
if (body.plan === 'plan1' || body.plan === 'planWeekly') {
|
|
13
13
|
body.mileagePackage = 'unlimited';
|
|
14
14
|
}
|
|
15
15
|
return body;
|
|
16
16
|
};
|
|
17
|
-
export const reserveVehicle = async (vehicleId, unprocessed)
|
|
17
|
+
export const reserveVehicle = async (vehicleId, unprocessed)=>{
|
|
18
18
|
const body = processBody(unprocessed);
|
|
19
19
|
// Agree to terms if not done yet
|
|
20
20
|
body.subscribingUser = await handleAgreeToTerms(body);
|
|
@@ -45,7 +45,7 @@ export const reserveVehicle = async (vehicleId, unprocessed) => {
|
|
|
45
45
|
const reservationResult = await fetchOrCreateReservation({
|
|
46
46
|
vehicle,
|
|
47
47
|
payer,
|
|
48
|
-
body
|
|
48
|
+
body
|
|
49
49
|
});
|
|
50
50
|
if (reservationResult.err) {
|
|
51
51
|
return reservationResult;
|
|
@@ -61,4 +61,3 @@ export const reserveVehicle = async (vehicleId, unprocessed) => {
|
|
|
61
61
|
}
|
|
62
62
|
return new Ok(reservation);
|
|
63
63
|
};
|
|
64
|
-
//# sourceMappingURL=reserve.js.map
|
|
@@ -1,2 +1 @@
|
|
|
1
|
-
export {};
|
|
2
|
-
//# sourceMappingURL=types.js.map
|
|
1
|
+
export { };
|