@driveflux/api-functions 1.0.45 → 1.0.47
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.
|
@@ -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
|
|
@@ -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.47",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
"./notion": {
|
|
@@ -73,20 +73,20 @@
|
|
|
73
73
|
],
|
|
74
74
|
"dependencies": {
|
|
75
75
|
"@casl/ability": "^6.7.3",
|
|
76
|
-
"@driveflux/auth": "4.0.
|
|
76
|
+
"@driveflux/auth": "4.0.35",
|
|
77
77
|
"@driveflux/billing": "8.0.3",
|
|
78
78
|
"@driveflux/config": "3.0.3",
|
|
79
|
-
"@driveflux/coupon": "9.0.
|
|
80
|
-
"@driveflux/db": "4.0.
|
|
81
|
-
"@driveflux/email": "7.0.
|
|
82
|
-
"@driveflux/email-templates": "1.0.
|
|
83
|
-
"@driveflux/engine": "1.0.
|
|
79
|
+
"@driveflux/coupon": "9.0.4",
|
|
80
|
+
"@driveflux/db": "4.0.4",
|
|
81
|
+
"@driveflux/email": "7.0.4",
|
|
82
|
+
"@driveflux/email-templates": "1.0.4",
|
|
83
|
+
"@driveflux/engine": "1.0.10",
|
|
84
84
|
"@driveflux/fetch": "8.0.0",
|
|
85
85
|
"@driveflux/format-money": "7.0.0",
|
|
86
86
|
"@driveflux/problem": "6.0.0",
|
|
87
87
|
"@driveflux/reporter": "7.0.1",
|
|
88
88
|
"@driveflux/result": "6.0.0",
|
|
89
|
-
"@driveflux/scheduler": "8.0.
|
|
89
|
+
"@driveflux/scheduler": "8.0.4",
|
|
90
90
|
"@driveflux/singleton": "3.0.0",
|
|
91
91
|
"@driveflux/subscription": "9.0.0",
|
|
92
92
|
"@driveflux/logger": "1.0.0",
|