@driveflux/api-functions 1.0.105 → 1.0.107
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/register.js
CHANGED
|
@@ -19,19 +19,25 @@ import { sendVerificationEmail } from './verifications.js';
|
|
|
19
19
|
export const RegistrationValidation = z.object({
|
|
20
20
|
firstName: z.string(),
|
|
21
21
|
lastName: z.string(),
|
|
22
|
-
phoneNumber: z.string().transform((s)
|
|
23
|
-
authMethod: z.enum([
|
|
22
|
+
phoneNumber: z.string().transform((s)=>s.replace(/[\s-]/g, '')),
|
|
23
|
+
authMethod: z.enum([
|
|
24
|
+
'mobile',
|
|
25
|
+
'email',
|
|
26
|
+
'facebook',
|
|
27
|
+
'google',
|
|
28
|
+
'apple'
|
|
29
|
+
]),
|
|
24
30
|
code: z.string().optional().nullable(),
|
|
25
31
|
keepCode: z.boolean().optional().default(false),
|
|
26
|
-
email: z.email().transform((s)
|
|
27
|
-
dateOfBirth: dateValidation
|
|
28
|
-
.refine((d) => {
|
|
32
|
+
email: z.email().transform((s)=>s.toLowerCase().trim()),
|
|
33
|
+
dateOfBirth: dateValidation.refine((d)=>{
|
|
29
34
|
return isAfter(d, subYears(new Date(), 25));
|
|
30
35
|
}, {
|
|
31
|
-
path: [
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
.
|
|
36
|
+
path: [
|
|
37
|
+
'dateOfBirth'
|
|
38
|
+
],
|
|
39
|
+
message: 'You must be at least 25 years old to signup for the service.'
|
|
40
|
+
}).optional(),
|
|
35
41
|
password: z.string().min(6).optional(),
|
|
36
42
|
noMarketing: z.boolean().optional(),
|
|
37
43
|
pageSource: z.string().optional().nullable(),
|
|
@@ -41,12 +47,14 @@ export const RegistrationValidation = z.object({
|
|
|
41
47
|
userAgent: z.string().optional(),
|
|
42
48
|
ipAddress: z.ipv4().or(z.ipv6()),
|
|
43
49
|
sessionToken: z.string().optional(),
|
|
44
|
-
sessionId: z.string().optional()
|
|
50
|
+
sessionId: z.string().optional()
|
|
45
51
|
});
|
|
46
|
-
export const handleRegister = async ({ noMarketing, password, authMethod, phoneNumber, code, keepCode, pageSource, skipPassword, fingerprint, termsVersion, userAgent, ipAddress, sessionToken, sessionId, ...body })
|
|
52
|
+
export const handleRegister = async ({ noMarketing, password, authMethod, phoneNumber, code, keepCode, pageSource, skipPassword, fingerprint, termsVersion, userAgent, ipAddress, sessionToken, sessionId, ...body })=>{
|
|
47
53
|
let phoneNumberVerified = false;
|
|
48
54
|
if (code) {
|
|
49
|
-
const tokenResult = await verifyToken(code, {
|
|
55
|
+
const tokenResult = await verifyToken(code, {
|
|
56
|
+
scope: 'verify-phone'
|
|
57
|
+
});
|
|
50
58
|
if (tokenResult.err) {
|
|
51
59
|
return new Err(makeProblem(PROBLEM_CONFLICT, 'Invalid OTP token'));
|
|
52
60
|
}
|
|
@@ -60,7 +68,9 @@ export const handleRegister = async ({ noMarketing, password, authMethod, phoneN
|
|
|
60
68
|
}
|
|
61
69
|
// Check if the user exists
|
|
62
70
|
const foundUser = await prisma.user.findFirst({
|
|
63
|
-
where: {
|
|
71
|
+
where: {
|
|
72
|
+
email: body.email
|
|
73
|
+
}
|
|
64
74
|
});
|
|
65
75
|
if (foundUser) {
|
|
66
76
|
return new Err(makeProblem(PROBLEM_CONFLICT, 'A user with this email address already exists. Did you mean to login?'));
|
|
@@ -77,10 +87,12 @@ export const handleRegister = async ({ noMarketing, password, authMethod, phoneN
|
|
|
77
87
|
preferredCurrency: 'MYR',
|
|
78
88
|
preferredLocale: 'en',
|
|
79
89
|
phoneNumberVerified,
|
|
80
|
-
groups: [
|
|
90
|
+
groups: [
|
|
91
|
+
'member'
|
|
92
|
+
],
|
|
81
93
|
signupParams: {
|
|
82
94
|
method: authMethod,
|
|
83
|
-
source: pageSource
|
|
95
|
+
source: pageSource
|
|
84
96
|
},
|
|
85
97
|
registrationComplete: true,
|
|
86
98
|
accounts: {
|
|
@@ -89,16 +101,16 @@ export const handleRegister = async ({ noMarketing, password, authMethod, phoneN
|
|
|
89
101
|
object: 'account',
|
|
90
102
|
type: 'default',
|
|
91
103
|
provider: 'credentials',
|
|
92
|
-
providerAccountId: id
|
|
93
|
-
}
|
|
104
|
+
providerAccountId: id
|
|
105
|
+
}
|
|
94
106
|
},
|
|
95
107
|
tokens: {
|
|
96
108
|
create: {
|
|
97
109
|
id: tokenId,
|
|
98
110
|
expiresAt: addDays(new Date(), 365),
|
|
99
|
-
scope: 'all'
|
|
100
|
-
}
|
|
101
|
-
}
|
|
111
|
+
scope: 'all'
|
|
112
|
+
}
|
|
113
|
+
}
|
|
102
114
|
};
|
|
103
115
|
const { user: _removeDueToNestedCreate, ...consentCreate } = getConsentCreateData({
|
|
104
116
|
user: userCreateData,
|
|
@@ -107,28 +119,28 @@ export const handleRegister = async ({ noMarketing, password, authMethod, phoneN
|
|
|
107
119
|
ipAddress: ipAddress,
|
|
108
120
|
userAgent: userAgent,
|
|
109
121
|
sessionToken: tokenId,
|
|
110
|
-
consentType: 'terms'
|
|
122
|
+
consentType: 'terms'
|
|
111
123
|
});
|
|
112
124
|
const finalCreateData = {
|
|
113
125
|
...userCreateData,
|
|
114
126
|
consents: {
|
|
115
|
-
create: consentCreate
|
|
116
|
-
}
|
|
127
|
+
create: consentCreate
|
|
128
|
+
}
|
|
117
129
|
};
|
|
118
130
|
// Find a host with the same email
|
|
119
131
|
const host = await prisma.host.findFirst({
|
|
120
132
|
where: {
|
|
121
|
-
email: finalCreateData.email
|
|
133
|
+
email: finalCreateData.email
|
|
122
134
|
},
|
|
123
135
|
select: {
|
|
124
|
-
id: true
|
|
125
|
-
}
|
|
136
|
+
id: true
|
|
137
|
+
}
|
|
126
138
|
});
|
|
127
139
|
if (host?.id) {
|
|
128
140
|
finalCreateData.host = {
|
|
129
141
|
connect: {
|
|
130
|
-
id: host.id
|
|
131
|
-
}
|
|
142
|
+
id: host.id
|
|
143
|
+
}
|
|
132
144
|
};
|
|
133
145
|
}
|
|
134
146
|
const user = await prisma.user.create({
|
|
@@ -136,20 +148,21 @@ export const handleRegister = async ({ noMarketing, password, authMethod, phoneN
|
|
|
136
148
|
include: {
|
|
137
149
|
tokens: {
|
|
138
150
|
where: {
|
|
139
|
-
id: tokenId
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
151
|
+
id: tokenId
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
143
155
|
});
|
|
144
156
|
// We don't want this to be a task. It should be instant
|
|
145
157
|
await sendVerificationEmail(user.id);
|
|
146
158
|
if (!noMarketing) {
|
|
147
|
-
await setContactInList(user.id, {
|
|
159
|
+
await setContactInList(user.id, {
|
|
160
|
+
generalMarketing: true
|
|
161
|
+
});
|
|
148
162
|
}
|
|
149
163
|
await refreshEmailPreferences(user.id);
|
|
150
164
|
return new Ok({
|
|
151
165
|
token: user.tokens[0],
|
|
152
|
-
user
|
|
166
|
+
user
|
|
153
167
|
});
|
|
154
168
|
};
|
|
155
|
-
//# sourceMappingURL=register.js.map
|
|
@@ -1,9 +1,9 @@
|
|
|
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
|
-
import { INVOICE_LINE_CYCLE, INVOICE_LINE_DEPOSIT, INVOICE_LINE_OTHER, INVOICE_LINE_RESERVATION_FEE, PURPOSE_FULL_BOOKING, PURPOSE_RESERVATION
|
|
6
|
+
import { INVOICE_LINE_CYCLE, INVOICE_LINE_DEPOSIT, INVOICE_LINE_OTHER, INVOICE_LINE_RESERVATION_FEE, PURPOSE_FULL_BOOKING, PURPOSE_RESERVATION } from '@driveflux/db/models/subscription';
|
|
7
7
|
import { userName } from '@driveflux/db/models/user';
|
|
8
8
|
import { vehicleName } from '@driveflux/db/models/vehicle';
|
|
9
9
|
import { biller, coupons } from '@driveflux/engine';
|
|
@@ -16,16 +16,15 @@ import { isAfter } from 'date-fns/isAfter';
|
|
|
16
16
|
import { subMinutes } from 'date-fns/subMinutes';
|
|
17
17
|
import { createScopedLogger } from '../create-logger.js';
|
|
18
18
|
const log = createScopedLogger('reservation:invoice');
|
|
19
|
-
export const updateReservationInvoiceIfNeeded = async (oldReservationInvoice, newParams)
|
|
19
|
+
export const updateReservationInvoiceIfNeeded = async (oldReservationInvoice, newParams)=>{
|
|
20
20
|
const couponIsDifferent = oldReservationInvoice.couponCode !== newParams.couponCode;
|
|
21
|
-
if (!couponIsDifferent &&
|
|
22
|
-
oldReservationInvoice.stripePaymentIntentId === newParams.paymentIntentId) {
|
|
21
|
+
if (!couponIsDifferent && oldReservationInvoice.stripePaymentIntentId === newParams.paymentIntentId) {
|
|
23
22
|
return new Ok(oldReservationInvoice);
|
|
24
23
|
}
|
|
25
24
|
// If the coupon or payment intent are differnt we should update the invoice
|
|
26
25
|
let update = {
|
|
27
26
|
stripePaymentIntentId: newParams.paymentIntentId,
|
|
28
|
-
couponCode: newParams.couponCode
|
|
27
|
+
couponCode: newParams.couponCode
|
|
29
28
|
};
|
|
30
29
|
// If the coupon is different, we need to apply the coupon to the invoice
|
|
31
30
|
if (couponIsDifferent) {
|
|
@@ -33,63 +32,56 @@ export const updateReservationInvoiceIfNeeded = async (oldReservationInvoice, ne
|
|
|
33
32
|
const result = await coupons.applyCouponToUnsavedInvoice(newParams.couponCode, oldReservationInvoice);
|
|
34
33
|
if (result.ok) {
|
|
35
34
|
const { discounts, ...rest } = result.val;
|
|
36
|
-
const toDisconnect = discounts
|
|
37
|
-
|
|
38
|
-
:
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
: undefined;
|
|
35
|
+
const toDisconnect = discounts ? oldReservationInvoice.discountIds?.filter((id)=>!discounts.connect?.some((d)=>d.id === id)) : undefined;
|
|
36
|
+
const disconnectClauses = toDisconnect?.length ? {
|
|
37
|
+
disconnect: toDisconnect.map((id)=>({
|
|
38
|
+
id
|
|
39
|
+
}))
|
|
40
|
+
} : undefined;
|
|
44
41
|
update = {
|
|
45
42
|
...update,
|
|
46
43
|
...rest,
|
|
47
|
-
...
|
|
48
|
-
|
|
49
|
-
discounts
|
|
50
|
-
|
|
51
|
-
...(disconnectClauses || {}),
|
|
52
|
-
},
|
|
44
|
+
...discounts || disconnectClauses ? {
|
|
45
|
+
discounts: {
|
|
46
|
+
...discounts || {},
|
|
47
|
+
...disconnectClauses || {}
|
|
53
48
|
}
|
|
54
|
-
|
|
49
|
+
} : {},
|
|
55
50
|
metadata: {
|
|
56
51
|
...update.metadata,
|
|
57
|
-
couponCode: newParams.couponCode
|
|
52
|
+
couponCode: newParams.couponCode
|
|
58
53
|
},
|
|
59
54
|
providerMetadata: {
|
|
60
55
|
...update.providerMetadata,
|
|
61
|
-
couponCode: newParams.couponCode
|
|
62
|
-
}
|
|
56
|
+
couponCode: newParams.couponCode
|
|
57
|
+
}
|
|
63
58
|
};
|
|
64
59
|
}
|
|
65
60
|
if (result.err && result.val.code !== PROBLEM_APPLICABLE_NOT_FOUND) {
|
|
66
61
|
return result;
|
|
67
62
|
}
|
|
68
|
-
}
|
|
69
|
-
else if (oldReservationInvoice.discountIds.length) {
|
|
63
|
+
} else if (oldReservationInvoice.discountIds.length) {
|
|
70
64
|
// No discounts, so we disconnect all of them
|
|
71
65
|
update = {
|
|
72
66
|
discounts: {
|
|
73
|
-
disconnect: oldReservationInvoice.discountIds.map((id)
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
67
|
+
disconnect: oldReservationInvoice.discountIds.map((id)=>({
|
|
68
|
+
id
|
|
69
|
+
}))
|
|
70
|
+
}
|
|
77
71
|
};
|
|
78
72
|
}
|
|
79
73
|
}
|
|
80
74
|
let updated = await wrapInResult(biller.updateInvoice(oldReservationInvoice.id, update));
|
|
81
75
|
// If the invoice was locked due to pending status and we're more than 1 minute old, we unlock it
|
|
82
76
|
if (updated.err && updated.val.code === 'invoice_pending') {
|
|
83
|
-
if ((oldReservationInvoice.lockedAt
|
|
84
|
-
isAfter(oldReservationInvoice.lockedAt, subMinutes(new Date(), 1))) ||
|
|
85
|
-
!oldReservationInvoice.lockedAt) {
|
|
77
|
+
if (oldReservationInvoice.lockedAt && isAfter(oldReservationInvoice.lockedAt, subMinutes(new Date(), 1)) || !oldReservationInvoice.lockedAt) {
|
|
86
78
|
updated = await wrapInResult(biller.updateInvoice(oldReservationInvoice.id, {
|
|
87
79
|
...update,
|
|
88
80
|
locked: false,
|
|
89
81
|
lockedAt: null,
|
|
90
82
|
lockReason: null,
|
|
91
83
|
unlockAt: null,
|
|
92
|
-
status: 'draft'
|
|
84
|
+
status: 'draft'
|
|
93
85
|
}));
|
|
94
86
|
}
|
|
95
87
|
}
|
|
@@ -98,7 +90,7 @@ export const updateReservationInvoiceIfNeeded = async (oldReservationInvoice, ne
|
|
|
98
90
|
}
|
|
99
91
|
return new Ok(updated.val);
|
|
100
92
|
};
|
|
101
|
-
export const createReservationInvoice = async ({ invoiceId, couponCode, plan, mileagePackage, referralCode, reservationId, paymentIntentId, freeReservation, freeReservationReason, analytics, vehicle, payer, subscribingUser, fullBooking
|
|
93
|
+
export const createReservationInvoice = async ({ invoiceId, couponCode, plan, mileagePackage, referralCode, reservationId, paymentIntentId, freeReservation, freeReservationReason, analytics, vehicle, payer, subscribingUser, fullBooking })=>{
|
|
102
94
|
if (freeReservation && !fullBooking) {
|
|
103
95
|
// It's a free reservation, so we don't need to add a coupon code
|
|
104
96
|
return await wrapInResult(biller.createInvoice(getInvoiceCreateDetails({
|
|
@@ -110,21 +102,17 @@ export const createReservationInvoice = async ({ invoiceId, couponCode, plan, mi
|
|
|
110
102
|
freeReservation,
|
|
111
103
|
freeReservationReason,
|
|
112
104
|
analytics,
|
|
113
|
-
reservationId
|
|
105
|
+
reservationId
|
|
114
106
|
})));
|
|
115
107
|
}
|
|
116
108
|
let finalCouponCode = couponCode;
|
|
117
109
|
// If we don't have a reservation coupon ID, we apply the referral discount if any
|
|
118
|
-
const referral = referralCode &&
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (!finalCouponCode &&
|
|
125
|
-
referral &&
|
|
126
|
-
referral.discountToReceiverCouponId &&
|
|
127
|
-
referral.discountToReceiverCouponApplicationName === 'reservationFee') {
|
|
110
|
+
const referral = referralCode && await prisma.referral.findUnique({
|
|
111
|
+
where: {
|
|
112
|
+
id: referralCode
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
if (!finalCouponCode && referral && referral.discountToReceiverCouponId && referral.discountToReceiverCouponApplicationName === 'reservationFee') {
|
|
128
116
|
const referralCoupon = await loadCoupon(referral.discountToReceiverCouponId);
|
|
129
117
|
if (referralCoupon) {
|
|
130
118
|
finalCouponCode = referralCoupon.code;
|
|
@@ -143,8 +131,8 @@ export const createReservationInvoice = async ({ invoiceId, couponCode, plan, mi
|
|
|
143
131
|
freeReservation,
|
|
144
132
|
freeReservationReason,
|
|
145
133
|
analytics,
|
|
146
|
-
reservationId
|
|
147
|
-
})
|
|
134
|
+
reservationId
|
|
135
|
+
})
|
|
148
136
|
};
|
|
149
137
|
if (fullBooking) {
|
|
150
138
|
invoiceCreateDetails.subscriptionPurpose = PURPOSE_FULL_BOOKING;
|
|
@@ -156,31 +144,32 @@ export const createReservationInvoice = async ({ invoiceId, couponCode, plan, mi
|
|
|
156
144
|
{
|
|
157
145
|
amount: vehiclePrice,
|
|
158
146
|
chargingFor: INVOICE_LINE_CYCLE,
|
|
159
|
-
description: `First payment for ${vehicleName(vehicle)}
|
|
147
|
+
description: `First payment for ${vehicleName(vehicle)}`
|
|
160
148
|
},
|
|
161
149
|
{
|
|
162
150
|
amount: vehiclePrice * config.taxRate,
|
|
163
151
|
chargingFor: INVOICE_LINE_OTHER,
|
|
164
|
-
description: `Processing fee for ${vehicleName(vehicle)}
|
|
152
|
+
description: `Processing fee for ${vehicleName(vehicle)}`
|
|
165
153
|
},
|
|
166
154
|
{
|
|
167
155
|
amount: vehiclePrice,
|
|
168
156
|
description: `Deposit for ${vehicleName(vehicle)}`,
|
|
169
|
-
chargingFor: INVOICE_LINE_DEPOSIT
|
|
170
|
-
}
|
|
157
|
+
chargingFor: INVOICE_LINE_DEPOSIT
|
|
158
|
+
}
|
|
171
159
|
];
|
|
172
|
-
}
|
|
173
|
-
else {
|
|
160
|
+
} else {
|
|
174
161
|
invoiceCreateDetails.lines = [
|
|
175
162
|
{
|
|
176
163
|
amount: reservationAmount,
|
|
177
164
|
description: `Reservation fee for ${vehicleName(vehicle)}`,
|
|
178
|
-
chargingFor: INVOICE_LINE_RESERVATION_FEE
|
|
179
|
-
}
|
|
165
|
+
chargingFor: INVOICE_LINE_RESERVATION_FEE
|
|
166
|
+
}
|
|
180
167
|
];
|
|
181
168
|
}
|
|
182
169
|
if (finalCouponCode) {
|
|
183
|
-
log.debug({
|
|
170
|
+
log.debug({
|
|
171
|
+
finalCouponCode
|
|
172
|
+
}, 'Applying coupon code to invoice');
|
|
184
173
|
const withCoupon = await coupons.applyCouponToUnsavedInvoice(finalCouponCode, invoiceCreateDetails, 'reservationFee', {
|
|
185
174
|
subscriptionPlans: plan,
|
|
186
175
|
subscriptionMileagePackages: mileagePackage,
|
|
@@ -188,24 +177,25 @@ export const createReservationInvoice = async ({ invoiceId, couponCode, plan, mi
|
|
|
188
177
|
vehicleMake: vehicle.make,
|
|
189
178
|
userId: subscribingUser.id,
|
|
190
179
|
businessId: payer.object === 'business' ? payer.id : undefined,
|
|
191
|
-
vehicleType: vehicle.type
|
|
180
|
+
vehicleType: vehicle.type
|
|
192
181
|
});
|
|
193
|
-
if (withCoupon.err &&
|
|
194
|
-
withCoupon.val.code !== PROBLEM_APPLICABLE_NOT_FOUND) {
|
|
182
|
+
if (withCoupon.err && withCoupon.val.code !== PROBLEM_APPLICABLE_NOT_FOUND) {
|
|
195
183
|
return withCoupon;
|
|
196
184
|
}
|
|
197
|
-
log.debug({
|
|
185
|
+
log.debug({
|
|
186
|
+
couponUpdates: withCoupon.val
|
|
187
|
+
}, 'Coupon applied to invoice');
|
|
198
188
|
if (withCoupon.ok) {
|
|
199
189
|
invoiceCreateDetails = {
|
|
200
190
|
...withCoupon.val,
|
|
201
191
|
metadata: {
|
|
202
192
|
...withCoupon.val.metadata,
|
|
203
|
-
couponCode: finalCouponCode
|
|
193
|
+
couponCode: finalCouponCode
|
|
204
194
|
},
|
|
205
195
|
providerMetadata: {
|
|
206
196
|
...withCoupon.val.providerMetadata,
|
|
207
|
-
couponCode: finalCouponCode
|
|
208
|
-
}
|
|
197
|
+
couponCode: finalCouponCode
|
|
198
|
+
}
|
|
209
199
|
};
|
|
210
200
|
}
|
|
211
201
|
}
|
|
@@ -217,9 +207,7 @@ export const createReservationInvoice = async ({ invoiceId, couponCode, plan, mi
|
|
|
217
207
|
if (paymentIntentResult.err) {
|
|
218
208
|
return new Err(makeProblem(PROBLEM_EXTERNAL, 'Payment intent not found'));
|
|
219
209
|
}
|
|
220
|
-
if (paymentIntentResult.val.amount !==
|
|
221
|
-
currency(biller.getCorrectedInvoiceData(invoiceCreateDetails).total)
|
|
222
|
-
.intValue) {
|
|
210
|
+
if (paymentIntentResult.val.amount !== currency(biller.getCorrectedInvoiceData(invoiceCreateDetails).total).intValue) {
|
|
223
211
|
return new Err(makeProblem(PROBLEM_EXTERNAL, 'Payment intent amount does not match invoice amount'));
|
|
224
212
|
}
|
|
225
213
|
}
|
|
@@ -227,15 +215,15 @@ export const createReservationInvoice = async ({ invoiceId, couponCode, plan, mi
|
|
|
227
215
|
...invoiceCreateDetails,
|
|
228
216
|
providerMetadata: {
|
|
229
217
|
...invoiceCreateDetails.providerMetadata,
|
|
230
|
-
invoiceId: invoiceCreateDetails.id
|
|
231
|
-
}
|
|
218
|
+
invoiceId: invoiceCreateDetails.id
|
|
219
|
+
}
|
|
232
220
|
}));
|
|
233
221
|
if (result.err) {
|
|
234
222
|
return result;
|
|
235
223
|
}
|
|
236
224
|
return new Ok(result.val);
|
|
237
225
|
};
|
|
238
|
-
const getInvoiceCreateDetails = ({ invoiceId, payer, date, reservationDiscountId, vehicle, plan, mileagePackage, couponCode, paymentIntentId, freeReservation, freeReservationReason, analytics, reservationId
|
|
226
|
+
const getInvoiceCreateDetails = ({ invoiceId, payer, date, reservationDiscountId, vehicle, plan, mileagePackage, couponCode, paymentIntentId, freeReservation, freeReservationReason, analytics, reservationId })=>{
|
|
239
227
|
return {
|
|
240
228
|
id: invoiceId || generateId('Invoice'),
|
|
241
229
|
taxPercentage: 0,
|
|
@@ -251,15 +239,13 @@ const getInvoiceCreateDetails = ({ invoiceId, payer, date, reservationDiscountId
|
|
|
251
239
|
description: 'Reservation fee for a FLUX subscription',
|
|
252
240
|
footer: `Invoice for a FLUX subscription reservation. Due by ${format(date ?? new Date())}`,
|
|
253
241
|
invoiceUrl: `${config.appUrl}/invoice-preview/${invoiceId}`,
|
|
254
|
-
...
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
},
|
|
260
|
-
},
|
|
242
|
+
...reservationDiscountId ? {
|
|
243
|
+
discounts: {
|
|
244
|
+
connect: {
|
|
245
|
+
id: reservationDiscountId
|
|
246
|
+
}
|
|
261
247
|
}
|
|
262
|
-
|
|
248
|
+
} : {},
|
|
263
249
|
lines: [],
|
|
264
250
|
subscriptionDetails: {
|
|
265
251
|
vehicleId: vehicle.id,
|
|
@@ -268,7 +254,7 @@ const getInvoiceCreateDetails = ({ invoiceId, payer, date, reservationDiscountId
|
|
|
268
254
|
variant: vehicle.variant,
|
|
269
255
|
year: vehicle.year,
|
|
270
256
|
plan: plan,
|
|
271
|
-
mileagePackage: mileagePackage
|
|
257
|
+
mileagePackage: mileagePackage
|
|
272
258
|
},
|
|
273
259
|
// Legacy field:
|
|
274
260
|
stripePaymentIntentId: paymentIntentId,
|
|
@@ -278,14 +264,18 @@ const getInvoiceCreateDetails = ({ invoiceId, payer, date, reservationDiscountId
|
|
|
278
264
|
mileagePackage,
|
|
279
265
|
vehicleId: vehicle.id,
|
|
280
266
|
vehicleName: vehicleName(vehicle),
|
|
281
|
-
...
|
|
282
|
-
|
|
267
|
+
...reservationId ? {
|
|
268
|
+
reservationId
|
|
269
|
+
} : {},
|
|
270
|
+
...couponCode ? {
|
|
271
|
+
couponCode
|
|
272
|
+
} : {}
|
|
283
273
|
},
|
|
284
274
|
autoRetry: {
|
|
285
275
|
enabled: false,
|
|
286
276
|
interval: 0,
|
|
287
277
|
maxAttempts: 0,
|
|
288
|
-
attempts: 0
|
|
278
|
+
attempts: 0
|
|
289
279
|
},
|
|
290
280
|
metadata: {
|
|
291
281
|
purpose: PURPOSE_RESERVATION,
|
|
@@ -294,11 +284,10 @@ const getInvoiceCreateDetails = ({ invoiceId, payer, date, reservationDiscountId
|
|
|
294
284
|
plan,
|
|
295
285
|
mileagePackage,
|
|
296
286
|
couponCode,
|
|
297
|
-
...
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
analytics
|
|
301
|
-
}
|
|
287
|
+
...freeReservation && freeReservationReason ? {
|
|
288
|
+
freeReservationReason
|
|
289
|
+
} : {},
|
|
290
|
+
analytics
|
|
291
|
+
}
|
|
302
292
|
};
|
|
303
293
|
};
|
|
304
|
-
//# sourceMappingURL=invoice.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
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@driveflux/api-functions",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.107",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
"./notion": {
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
"@driveflux/db": "4.0.32",
|
|
81
81
|
"@driveflux/email": "7.0.33",
|
|
82
82
|
"@driveflux/email-templates": "1.1.3",
|
|
83
|
-
"@driveflux/engine": "1.0.
|
|
83
|
+
"@driveflux/engine": "1.0.91",
|
|
84
84
|
"@driveflux/fetch": "8.0.0",
|
|
85
85
|
"@driveflux/format-money": "7.0.0",
|
|
86
86
|
"@driveflux/logger": "1.0.1",
|