@darraghor/nest-backend-libs 5.7.4 → 6.0.0
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/README.md +87 -36
- package/dist/cli/commands/WriteApiClientCommand.js +1 -2
- package/dist/cli/commands/WriteApiClientCommand.js.map +1 -1
- package/dist/database-sqlite/SqliteTypeOrmConfigurationProvider.js +5 -4
- package/dist/database-sqlite/SqliteTypeOrmConfigurationProvider.js.map +1 -1
- package/dist/exports/authorization.d.ts +14 -0
- package/dist/exports/authorization.js +12 -0
- package/dist/exports/authorization.js.map +1 -0
- package/dist/exports/core.d.ts +11 -0
- package/dist/exports/core.js +10 -0
- package/dist/exports/core.js.map +1 -0
- package/dist/exports/database.d.ts +5 -0
- package/dist/exports/database.js +6 -0
- package/dist/exports/database.js.map +1 -0
- package/dist/exports/email.d.ts +4 -0
- package/dist/exports/email.js +4 -0
- package/dist/exports/email.js.map +1 -0
- package/dist/exports/invitations.d.ts +6 -0
- package/dist/exports/invitations.js +6 -0
- package/dist/exports/invitations.js.map +1 -0
- package/dist/exports/organisations.d.ts +11 -0
- package/dist/exports/organisations.js +12 -0
- package/dist/exports/organisations.js.map +1 -0
- package/dist/exports/stripe-legacy.d.ts +6 -0
- package/dist/exports/stripe-legacy.js +7 -0
- package/dist/exports/stripe-legacy.js.map +1 -0
- package/dist/exports/stripe.d.ts +11 -0
- package/dist/exports/stripe.js +9 -0
- package/dist/exports/stripe.js.map +1 -0
- package/dist/exports/twitter.d.ts +5 -0
- package/dist/exports/twitter.js +5 -0
- package/dist/exports/twitter.js.map +1 -0
- package/dist/exports/users.d.ts +10 -0
- package/dist/exports/users.js +11 -0
- package/dist/exports/users.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -1
- package/dist/organisation-subscriptions/entities/organisation-subscription.entity.js +2 -1
- package/dist/organisation-subscriptions/entities/organisation-subscription.entity.js.map +1 -1
- package/dist/stripe-client/StripeClientConfigurationService.js +1 -0
- package/dist/stripe-client/StripeClientConfigurationService.js.map +1 -1
- package/dist/stripe-client/StripeClientProvider.js +1 -1
- package/dist/stripe-client/StripeClientProvider.js.map +1 -1
- package/dist/stripe-client/controllers/stripe-checkout-controller.d.ts +1 -0
- package/dist/stripe-client/controllers/stripe-checkout-controller.js +1 -0
- package/dist/stripe-client/controllers/stripe-checkout-controller.js.map +1 -1
- package/dist/stripe-client/controllers/stripe-events-controller.js +1 -0
- package/dist/stripe-client/controllers/stripe-events-controller.js.map +1 -1
- package/dist/stripe-client/controllers/stripe-unauthenticated-checkout-controller.js +1 -0
- package/dist/stripe-client/controllers/stripe-unauthenticated-checkout-controller.js.map +1 -1
- package/dist/stripe-client/controllers/stripe-webhook-controller.js +1 -4
- package/dist/stripe-client/controllers/stripe-webhook-controller.js.map +1 -1
- package/dist/stripe-client/services/auth-stripe-checkout.service.js +1 -0
- package/dist/stripe-client/services/auth-stripe-checkout.service.js.map +1 -1
- package/dist/stripe-client/services/queued-payment-event.handler.d.ts +1 -0
- package/dist/stripe-client/services/queued-payment-event.handler.js +2 -0
- package/dist/stripe-client/services/queued-payment-event.handler.js.map +1 -1
- package/dist/stripe-client/services/stripe-checkout.service.d.ts +1 -0
- package/dist/stripe-client/services/stripe-checkout.service.js +2 -0
- package/dist/stripe-client/services/stripe-checkout.service.js.map +1 -1
- package/dist/stripe-client/stripe-account.module.d.ts +1 -0
- package/dist/stripe-client/stripe-account.module.js +2 -0
- package/dist/stripe-client/stripe-account.module.js.map +1 -1
- package/dist/stripe-client/stripe-account.options.d.ts +2 -0
- package/dist/stripe-client/stripe-account.options.js.map +1 -1
- package/dist/stripe-payments/controllers/secure-stripe-checkout.controller.d.ts +10 -0
- package/dist/stripe-payments/controllers/secure-stripe-checkout.controller.js +65 -0
- package/dist/stripe-payments/controllers/secure-stripe-checkout.controller.js.map +1 -0
- package/dist/stripe-payments/controllers/secure-stripe-events.controller.d.ts +9 -0
- package/dist/stripe-payments/controllers/secure-stripe-events.controller.js +65 -0
- package/dist/stripe-payments/controllers/secure-stripe-events.controller.js.map +1 -0
- package/dist/stripe-payments/controllers/secure-stripe-webhook.controller.d.ts +14 -0
- package/dist/stripe-payments/controllers/secure-stripe-webhook.controller.js +112 -0
- package/dist/stripe-payments/controllers/secure-stripe-webhook.controller.js.map +1 -0
- package/dist/stripe-payments/entities/stripe-checkout-attempt.entity.d.ts +15 -0
- package/dist/stripe-payments/entities/stripe-checkout-attempt.entity.js +88 -0
- package/dist/stripe-payments/entities/stripe-checkout-attempt.entity.js.map +1 -0
- package/dist/stripe-payments/entities/stripe-payment-event.entity.d.ts +16 -0
- package/dist/stripe-payments/entities/stripe-payment-event.entity.js +101 -0
- package/dist/stripe-payments/entities/stripe-payment-event.entity.js.map +1 -0
- package/dist/stripe-payments/entities/stripe-payment-state.entity.d.ts +9 -0
- package/dist/stripe-payments/entities/stripe-payment-state.entity.js +53 -0
- package/dist/stripe-payments/entities/stripe-payment-state.entity.js.map +1 -0
- package/dist/stripe-payments/models/secure-checkout.dto.d.ts +14 -0
- package/dist/stripe-payments/models/secure-checkout.dto.js +105 -0
- package/dist/stripe-payments/models/secure-checkout.dto.js.map +1 -0
- package/dist/stripe-payments/models/secure-payment-response.dto.d.ts +7 -0
- package/dist/stripe-payments/models/secure-payment-response.dto.js +30 -0
- package/dist/stripe-payments/models/secure-payment-response.dto.js.map +1 -0
- package/dist/stripe-payments/services/default-stripe-payments-entitlement-store.d.ts +12 -0
- package/dist/stripe-payments/services/default-stripe-payments-entitlement-store.js +49 -0
- package/dist/stripe-payments/services/default-stripe-payments-entitlement-store.js.map +1 -0
- package/dist/stripe-payments/services/secure-stripe-checkout.service.d.ts +24 -0
- package/dist/stripe-payments/services/secure-stripe-checkout.service.js +213 -0
- package/dist/stripe-payments/services/secure-stripe-checkout.service.js.map +1 -0
- package/dist/stripe-payments/services/secure-stripe-client.provider.d.ts +2 -0
- package/dist/stripe-payments/services/secure-stripe-client.provider.js +15 -0
- package/dist/stripe-payments/services/secure-stripe-client.provider.js.map +1 -0
- package/dist/stripe-payments/services/secure-stripe-product.service.d.ts +9 -0
- package/dist/stripe-payments/services/secure-stripe-product.service.js +54 -0
- package/dist/stripe-payments/services/secure-stripe-product.service.js.map +1 -0
- package/dist/stripe-payments/services/secure-stripe-webhook.service.d.ts +14 -0
- package/dist/stripe-payments/services/secure-stripe-webhook.service.js +58 -0
- package/dist/stripe-payments/services/secure-stripe-webhook.service.js.map +1 -0
- package/dist/stripe-payments/services/secure-subscription.service.d.ts +18 -0
- package/dist/stripe-payments/services/secure-subscription.service.js +217 -0
- package/dist/stripe-payments/services/secure-subscription.service.js.map +1 -0
- package/dist/stripe-payments/services/stripe-payment-event.processor.d.ts +15 -0
- package/dist/stripe-payments/services/stripe-payment-event.processor.js +78 -0
- package/dist/stripe-payments/services/stripe-payment-event.processor.js.map +1 -0
- package/dist/stripe-payments/services/stripe-payment-event.service.d.ts +20 -0
- package/dist/stripe-payments/services/stripe-payment-event.service.js +178 -0
- package/dist/stripe-payments/services/stripe-payment-event.service.js.map +1 -0
- package/dist/stripe-payments/services/stripe-payment-fulfillment.service.d.ts +31 -0
- package/dist/stripe-payments/services/stripe-payment-fulfillment.service.js +331 -0
- package/dist/stripe-payments/services/stripe-payment-fulfillment.service.js.map +1 -0
- package/dist/stripe-payments/services/stripe-payments-operations.service.d.ts +25 -0
- package/dist/stripe-payments/services/stripe-payments-operations.service.js +59 -0
- package/dist/stripe-payments/services/stripe-payments-operations.service.js.map +1 -0
- package/dist/stripe-payments/services/stripe-payments-telemetry.service.d.ts +7 -0
- package/dist/stripe-payments/services/stripe-payments-telemetry.service.js +40 -0
- package/dist/stripe-payments/services/stripe-payments-telemetry.service.js.map +1 -0
- package/dist/stripe-payments/stripe-payment.config.d.ts +19 -0
- package/dist/stripe-payments/stripe-payment.config.js +98 -0
- package/dist/stripe-payments/stripe-payment.config.js.map +1 -0
- package/dist/stripe-payments/stripe-payment.config.test.d.ts +1 -0
- package/dist/stripe-payments/stripe-payment.config.test.js +59 -0
- package/dist/stripe-payments/stripe-payment.config.test.js.map +1 -0
- package/dist/stripe-payments/stripe-payments.extensions.d.ts +69 -0
- package/dist/stripe-payments/stripe-payments.extensions.js +30 -0
- package/dist/stripe-payments/stripe-payments.extensions.js.map +1 -0
- package/dist/stripe-payments/stripe-payments.module.d.ts +12 -0
- package/dist/stripe-payments/stripe-payments.module.js +118 -0
- package/dist/stripe-payments/stripe-payments.module.js.map +1 -0
- package/dist/stripe-payments/stripe-payments.options.d.ts +21 -0
- package/dist/stripe-payments/stripe-payments.options.js +2 -0
- package/dist/stripe-payments/stripe-payments.options.js.map +1 -0
- package/dist/typeorm/entities.d.ts +15 -0
- package/dist/typeorm/entities.js +32 -0
- package/dist/typeorm/entities.js.map +1 -0
- package/package.json +85 -38
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
11
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
|
+
};
|
|
13
|
+
var SecureStripeCheckoutService_1;
|
|
14
|
+
import { ConflictException, Inject, Injectable, Logger, ServiceUnavailableException, } from "@nestjs/common";
|
|
15
|
+
import Stripe from "stripe";
|
|
16
|
+
import { InjectRepository } from "@nestjs/typeorm";
|
|
17
|
+
import { QueryFailedError, Repository } from "typeorm";
|
|
18
|
+
import { StripeCheckoutAttempt } from "../entities/stripe-checkout-attempt.entity.js";
|
|
19
|
+
import { SecureStripeProductService } from "./secure-stripe-product.service.js";
|
|
20
|
+
import { STRIPE_PAYMENTS_ACCESS_POLICY, STRIPE_PAYMENTS_ENTITLEMENT_STORE, } from "../stripe-payments.extensions.js";
|
|
21
|
+
import { StripePaymentsTelemetryService } from "./stripe-payments-telemetry.service.js";
|
|
22
|
+
const idempotencyKeyPattern = /^[A-Za-z0-9._-]{8,255}$/;
|
|
23
|
+
let SecureStripeCheckoutService = SecureStripeCheckoutService_1 = class SecureStripeCheckoutService {
|
|
24
|
+
stripe;
|
|
25
|
+
attemptRepository;
|
|
26
|
+
productService;
|
|
27
|
+
entitlementStore;
|
|
28
|
+
accessPolicy;
|
|
29
|
+
telemetry;
|
|
30
|
+
logger = new Logger(SecureStripeCheckoutService_1.name);
|
|
31
|
+
constructor(stripe, attemptRepository, productService, entitlementStore, accessPolicy, telemetry) {
|
|
32
|
+
this.stripe = stripe;
|
|
33
|
+
this.attemptRepository = attemptRepository;
|
|
34
|
+
this.productService = productService;
|
|
35
|
+
this.entitlementStore = entitlementStore;
|
|
36
|
+
this.accessPolicy = accessPolicy;
|
|
37
|
+
this.telemetry = telemetry;
|
|
38
|
+
}
|
|
39
|
+
async createCheckoutSession(request, user, idempotencyKey) {
|
|
40
|
+
const key = this.requireIdempotencyKey(idempotencyKey);
|
|
41
|
+
await this.accessPolicy.assertCanManageOrganisation({
|
|
42
|
+
organisationUuid: request.organisationUuid,
|
|
43
|
+
user,
|
|
44
|
+
operation: "checkout",
|
|
45
|
+
});
|
|
46
|
+
const product = this.productService.getByKey(request.productKey);
|
|
47
|
+
const successUrl = this.productService.buildRedirectUrl(request.successFrontendPath);
|
|
48
|
+
const cancelUrl = request.cancelFrontendPath
|
|
49
|
+
? this.productService.buildRedirectUrl(request.cancelFrontendPath)
|
|
50
|
+
: undefined;
|
|
51
|
+
const existingAttempt = await this.attemptRepository.findOne({
|
|
52
|
+
where: { idempotencyKey: key },
|
|
53
|
+
});
|
|
54
|
+
if (existingAttempt) {
|
|
55
|
+
this.assertMatchingAttempt(existingAttempt, request, user, product.priceId);
|
|
56
|
+
if (existingAttempt.status === "created" &&
|
|
57
|
+
existingAttempt.stripeSessionId &&
|
|
58
|
+
existingAttempt.stripeSessionUrl) {
|
|
59
|
+
return {
|
|
60
|
+
stripeSessionId: existingAttempt.stripeSessionId,
|
|
61
|
+
stripeSessionUrl: existingAttempt.stripeSessionUrl,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
if (existingAttempt.updatedDate.getTime() >
|
|
65
|
+
Date.now() - 10 * 60 * 1000) {
|
|
66
|
+
throw new ConflictException("Checkout attempt is already in progress");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const attempt = existingAttempt ??
|
|
70
|
+
this.attemptRepository.create({
|
|
71
|
+
idempotencyKey: key,
|
|
72
|
+
organisationUuid: request.organisationUuid,
|
|
73
|
+
userUuid: user.uuid,
|
|
74
|
+
productKey: product.key,
|
|
75
|
+
priceId: product.priceId,
|
|
76
|
+
mode: product.mode,
|
|
77
|
+
status: "creating",
|
|
78
|
+
});
|
|
79
|
+
attempt.status = "creating";
|
|
80
|
+
attempt.errorMessage = undefined;
|
|
81
|
+
try {
|
|
82
|
+
await this.attemptRepository.save(attempt);
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
if (!this.isUniqueViolation(error)) {
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
const concurrentAttempt = await this.attemptRepository.findOne({
|
|
89
|
+
where: { idempotencyKey: key },
|
|
90
|
+
});
|
|
91
|
+
if (!concurrentAttempt) {
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
this.assertMatchingAttempt(concurrentAttempt, request, user, product.priceId);
|
|
95
|
+
if (concurrentAttempt.status === "created" &&
|
|
96
|
+
concurrentAttempt.stripeSessionId &&
|
|
97
|
+
concurrentAttempt.stripeSessionUrl) {
|
|
98
|
+
return {
|
|
99
|
+
stripeSessionId: concurrentAttempt.stripeSessionId,
|
|
100
|
+
stripeSessionUrl: concurrentAttempt.stripeSessionUrl,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
throw new ConflictException("Checkout attempt is already in progress");
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
const metadata = {
|
|
107
|
+
organisationUuid: request.organisationUuid,
|
|
108
|
+
userUuid: user.uuid,
|
|
109
|
+
productKey: product.key,
|
|
110
|
+
internalSku: product.internalSku,
|
|
111
|
+
checkoutAttemptId: String(attempt.id),
|
|
112
|
+
};
|
|
113
|
+
const session = await this.stripe.checkout.sessions.create({
|
|
114
|
+
mode: product.mode,
|
|
115
|
+
client_reference_id: String(attempt.id),
|
|
116
|
+
line_items: [{ price: product.priceId, quantity: 1 }],
|
|
117
|
+
success_url: successUrl,
|
|
118
|
+
cancel_url: cancelUrl,
|
|
119
|
+
metadata,
|
|
120
|
+
...(product.mode === "subscription"
|
|
121
|
+
? { subscription_data: { metadata } }
|
|
122
|
+
: { payment_intent_data: { metadata } }),
|
|
123
|
+
}, { idempotencyKey: key });
|
|
124
|
+
if (!session.url) {
|
|
125
|
+
throw new Error("Stripe did not return a Checkout URL");
|
|
126
|
+
}
|
|
127
|
+
attempt.status = "created";
|
|
128
|
+
attempt.stripeSessionId = session.id;
|
|
129
|
+
attempt.stripeSessionUrl = session.url;
|
|
130
|
+
await this.attemptRepository.save(attempt);
|
|
131
|
+
await this.telemetry.record({
|
|
132
|
+
name: "checkout.created",
|
|
133
|
+
attemptId: attempt.id,
|
|
134
|
+
organisationUuid: attempt.organisationUuid,
|
|
135
|
+
productKey: attempt.productKey,
|
|
136
|
+
stripeSessionId: session.id,
|
|
137
|
+
});
|
|
138
|
+
return {
|
|
139
|
+
stripeSessionId: session.id,
|
|
140
|
+
stripeSessionUrl: session.url,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
attempt.status = "failed";
|
|
145
|
+
const errorMessage = error instanceof Error
|
|
146
|
+
? error.message.slice(0, 500)
|
|
147
|
+
: "Stripe error";
|
|
148
|
+
attempt.errorMessage = errorMessage;
|
|
149
|
+
await this.attemptRepository.save(attempt);
|
|
150
|
+
await this.telemetry.record({
|
|
151
|
+
name: "checkout.failed",
|
|
152
|
+
attemptId: attempt.id,
|
|
153
|
+
organisationUuid: attempt.organisationUuid,
|
|
154
|
+
productKey: attempt.productKey,
|
|
155
|
+
error: errorMessage,
|
|
156
|
+
});
|
|
157
|
+
this.logger.error("Stripe Checkout session creation failed", {
|
|
158
|
+
attemptId: attempt.id,
|
|
159
|
+
productKey: product.key,
|
|
160
|
+
error: error instanceof Error ? error.message : String(error),
|
|
161
|
+
});
|
|
162
|
+
throw new ServiceUnavailableException("Unable to start checkout right now. Please try again.");
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
async createCustomerPortalSession(request, user, idempotencyKey) {
|
|
166
|
+
const key = this.requireIdempotencyKey(idempotencyKey);
|
|
167
|
+
const subscription = await this.entitlementStore.findByUuid(request.subscriptionRecordUuid);
|
|
168
|
+
await this.accessPolicy.assertCanManageOrganisation({
|
|
169
|
+
organisationUuid: subscription.organisationUuid,
|
|
170
|
+
user,
|
|
171
|
+
operation: "customer-portal",
|
|
172
|
+
});
|
|
173
|
+
if (!/^cus_[A-Za-z0-9]+$/.test(subscription.paymentSystemCustomerId)) {
|
|
174
|
+
throw new ConflictException("This subscription has no valid Stripe customer");
|
|
175
|
+
}
|
|
176
|
+
const session = await this.stripe.billingPortal.sessions.create({
|
|
177
|
+
customer: subscription.paymentSystemCustomerId,
|
|
178
|
+
return_url: this.productService.buildRedirectUrl(request.returnUrl),
|
|
179
|
+
}, { idempotencyKey: key });
|
|
180
|
+
return { sessionUrl: session.url };
|
|
181
|
+
}
|
|
182
|
+
requireIdempotencyKey(idempotencyKey) {
|
|
183
|
+
if (!idempotencyKey || !idempotencyKeyPattern.test(idempotencyKey)) {
|
|
184
|
+
throw new ConflictException("An Idempotency-Key header is required for payment requests");
|
|
185
|
+
}
|
|
186
|
+
return idempotencyKey;
|
|
187
|
+
}
|
|
188
|
+
assertMatchingAttempt(attempt, request, user, priceId) {
|
|
189
|
+
if (attempt.organisationUuid !== request.organisationUuid ||
|
|
190
|
+
attempt.userUuid !== user.uuid ||
|
|
191
|
+
attempt.productKey !== request.productKey ||
|
|
192
|
+
attempt.priceId !== priceId) {
|
|
193
|
+
throw new ConflictException("The Idempotency-Key was already used for another checkout");
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
isUniqueViolation(error) {
|
|
197
|
+
return (error instanceof QueryFailedError &&
|
|
198
|
+
error
|
|
199
|
+
.driverError.code === "23505");
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
SecureStripeCheckoutService = SecureStripeCheckoutService_1 = __decorate([
|
|
203
|
+
Injectable(),
|
|
204
|
+
__param(0, Inject("SecureStripeClient")),
|
|
205
|
+
__param(1, InjectRepository(StripeCheckoutAttempt)),
|
|
206
|
+
__param(3, Inject(STRIPE_PAYMENTS_ENTITLEMENT_STORE)),
|
|
207
|
+
__param(4, Inject(STRIPE_PAYMENTS_ACCESS_POLICY)),
|
|
208
|
+
__metadata("design:paramtypes", [Stripe,
|
|
209
|
+
Repository,
|
|
210
|
+
SecureStripeProductService, Object, Object, StripePaymentsTelemetryService])
|
|
211
|
+
], SecureStripeCheckoutService);
|
|
212
|
+
export { SecureStripeCheckoutService };
|
|
213
|
+
//# sourceMappingURL=secure-stripe-checkout.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secure-stripe-checkout.service.js","sourceRoot":"","sources":["../../../src/stripe-payments/services/secure-stripe-checkout.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import Stripe from "stripe";
|
|
2
|
+
import { SECURE_STRIPE_CONFIGURATION, } from "../stripe-payment.config.js";
|
|
3
|
+
export const secureStripeClientProvider = {
|
|
4
|
+
provide: "SecureStripeClient",
|
|
5
|
+
useFactory: (configuration) => new Stripe(configuration.accessToken, {
|
|
6
|
+
apiVersion: Stripe.API_VERSION,
|
|
7
|
+
appInfo: {
|
|
8
|
+
name: "Miller Secure Payments",
|
|
9
|
+
url: "https://usemiller.dev",
|
|
10
|
+
},
|
|
11
|
+
maxNetworkRetries: 2,
|
|
12
|
+
}),
|
|
13
|
+
inject: [SECURE_STRIPE_CONFIGURATION],
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=secure-stripe-client.provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secure-stripe-client.provider.js","sourceRoot":"","sources":["../../../src/stripe-payments/services/secure-stripe-client.provider.ts"],"names":[],"mappings":"AACA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EACH,2BAA2B,GAE9B,MAAM,6BAA6B,CAAC;AAErC,MAAM,CAAC,MAAM,0BAA0B,GAAa;IAChD,OAAO,EAAE,oBAAoB;IAC7B,UAAU,EAAE,CAAC,aAAwC,EAAU,EAAE,CAC7D,IAAI,MAAM,CAAC,aAAa,CAAC,WAAW,EAAE;QAClC,UAAU,EAAE,MAAM,CAAC,WAAW;QAC9B,OAAO,EAAE;YACL,IAAI,EAAE,wBAAwB;YAC9B,GAAG,EAAE,uBAAuB;SAC/B;QACD,iBAAiB,EAAE,CAAC;KACvB,CAAC;IACN,MAAM,EAAE,CAAC,2BAA2B,CAAC;CACxC,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { SecureStripeConfiguration, SecureStripeProduct } from "../stripe-payment.config.js";
|
|
2
|
+
export declare class SecureStripeProductService {
|
|
3
|
+
private readonly configuration;
|
|
4
|
+
constructor(configuration: SecureStripeConfiguration);
|
|
5
|
+
getByKey(productKey: string): SecureStripeProduct;
|
|
6
|
+
getByPriceId(priceId: string): SecureStripeProduct;
|
|
7
|
+
getConfiguredProducts(): readonly SecureStripeProduct[];
|
|
8
|
+
buildRedirectUrl(path: string): string;
|
|
9
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
11
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
|
+
};
|
|
13
|
+
import { BadRequestException, Inject, Injectable } from "@nestjs/common";
|
|
14
|
+
import { SECURE_STRIPE_CONFIGURATION, } from "../stripe-payment.config.js";
|
|
15
|
+
let SecureStripeProductService = class SecureStripeProductService {
|
|
16
|
+
configuration;
|
|
17
|
+
constructor(configuration) {
|
|
18
|
+
this.configuration = configuration;
|
|
19
|
+
}
|
|
20
|
+
getByKey(productKey) {
|
|
21
|
+
const product = this.configuration.products.get(productKey);
|
|
22
|
+
if (!product) {
|
|
23
|
+
throw new BadRequestException("Unknown payment product");
|
|
24
|
+
}
|
|
25
|
+
return product;
|
|
26
|
+
}
|
|
27
|
+
getByPriceId(priceId) {
|
|
28
|
+
const product = [...this.configuration.products.values()].find((candidate) => candidate.priceId === priceId);
|
|
29
|
+
if (!product) {
|
|
30
|
+
throw new Error("Stripe returned a price outside the configured catalog");
|
|
31
|
+
}
|
|
32
|
+
return product;
|
|
33
|
+
}
|
|
34
|
+
getConfiguredProducts() {
|
|
35
|
+
return [...this.configuration.products.values()];
|
|
36
|
+
}
|
|
37
|
+
buildRedirectUrl(path) {
|
|
38
|
+
const url = new URL(path, `${this.configuration.redirectsBaseUrl}/`);
|
|
39
|
+
const baseUrl = new URL(`${this.configuration.redirectsBaseUrl}/`);
|
|
40
|
+
if (url.origin !== baseUrl.origin ||
|
|
41
|
+
!path.startsWith("/") ||
|
|
42
|
+
path.startsWith("//")) {
|
|
43
|
+
throw new BadRequestException("Redirect path must remain on the configured application origin");
|
|
44
|
+
}
|
|
45
|
+
return url.toString();
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
SecureStripeProductService = __decorate([
|
|
49
|
+
Injectable(),
|
|
50
|
+
__param(0, Inject(SECURE_STRIPE_CONFIGURATION)),
|
|
51
|
+
__metadata("design:paramtypes", [Object])
|
|
52
|
+
], SecureStripeProductService);
|
|
53
|
+
export { SecureStripeProductService };
|
|
54
|
+
//# sourceMappingURL=secure-stripe-product.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secure-stripe-product.service.js","sourceRoot":"","sources":["../../../src/stripe-payments/services/secure-stripe-product.service.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,OAAO,EAAC,mBAAmB,EAAE,MAAM,EAAE,UAAU,EAAC,MAAM,gBAAgB,CAAC;AACvE,OAAO,EACH,2BAA2B,GAG9B,MAAM,6BAA6B,CAAC;AAG9B,IAAM,0BAA0B,GAAhC,MAAM,0BAA0B;IAGd;IAFrB,YAEqB,aAAwC;QAAxC,kBAAa,GAAb,aAAa,CAA2B;IAC1D,CAAC;IAEJ,QAAQ,CAAC,UAAkB;QACvB,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC5D,IAAI,CAAC,OAAO,EAAE,CAAC;YACX,MAAM,IAAI,mBAAmB,CAAC,yBAAyB,CAAC,CAAC;QAC7D,CAAC;QACD,OAAO,OAAO,CAAC;IACnB,CAAC;IAED,YAAY,CAAC,OAAe;QACxB,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAC1D,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,OAAO,KAAK,OAAO,CAC/C,CAAC;QACF,IAAI,CAAC,OAAO,EAAE,CAAC;YACX,MAAM,IAAI,KAAK,CACX,wDAAwD,CAC3D,CAAC;QACN,CAAC;QACD,OAAO,OAAO,CAAC;IACnB,CAAC;IAED,qBAAqB;QACjB,OAAO,CAAC,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IACrD,CAAC;IAED,gBAAgB,CAAC,IAAY;QACzB,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC,gBAAgB,GAAG,CAAC,CAAC;QACrE,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,GAAG,IAAI,CAAC,aAAa,CAAC,gBAAgB,GAAG,CAAC,CAAC;QACnE,IACI,GAAG,CAAC,MAAM,KAAK,OAAO,CAAC,MAAM;YAC7B,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YACrB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EACvB,CAAC;YACC,MAAM,IAAI,mBAAmB,CACzB,gEAAgE,CACnE,CAAC;QACN,CAAC;QACD,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC;IAC1B,CAAC;CACJ,CAAA;AA5CY,0BAA0B;IADtC,UAAU,EAAE;IAGJ,WAAA,MAAM,CAAC,2BAA2B,CAAC,CAAA;;GAF/B,0BAA0B,CA4CtC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import Stripe from "stripe";
|
|
2
|
+
import { SecureStripeConfiguration } from "../stripe-payment.config.js";
|
|
3
|
+
import { StripePaymentEventService } from "./stripe-payment-event.service.js";
|
|
4
|
+
export interface RawStripeWebhookRequest {
|
|
5
|
+
headers: Record<string, string | string[] | undefined>;
|
|
6
|
+
rawBody?: Buffer;
|
|
7
|
+
}
|
|
8
|
+
export declare class SecureStripeWebhookService {
|
|
9
|
+
private readonly stripe;
|
|
10
|
+
private readonly configuration;
|
|
11
|
+
private readonly paymentEventService;
|
|
12
|
+
constructor(stripe: Stripe, configuration: SecureStripeConfiguration, paymentEventService: StripePaymentEventService);
|
|
13
|
+
handleWebhook(request: RawStripeWebhookRequest): Promise<void>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
11
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
|
+
};
|
|
13
|
+
import { BadRequestException, Inject, Injectable, ServiceUnavailableException, } from "@nestjs/common";
|
|
14
|
+
import Stripe from "stripe";
|
|
15
|
+
import { SECURE_STRIPE_CONFIGURATION, } from "../stripe-payment.config.js";
|
|
16
|
+
import { StripePaymentEventService } from "./stripe-payment-event.service.js";
|
|
17
|
+
let SecureStripeWebhookService = class SecureStripeWebhookService {
|
|
18
|
+
stripe;
|
|
19
|
+
configuration;
|
|
20
|
+
paymentEventService;
|
|
21
|
+
constructor(stripe, configuration, paymentEventService) {
|
|
22
|
+
this.stripe = stripe;
|
|
23
|
+
this.configuration = configuration;
|
|
24
|
+
this.paymentEventService = paymentEventService;
|
|
25
|
+
}
|
|
26
|
+
async handleWebhook(request) {
|
|
27
|
+
if (!request.rawBody) {
|
|
28
|
+
throw new BadRequestException("Webhook body was not received as raw bytes");
|
|
29
|
+
}
|
|
30
|
+
const signatureHeader = request.headers["stripe-signature"];
|
|
31
|
+
if (typeof signatureHeader !== "string") {
|
|
32
|
+
throw new BadRequestException("Missing Stripe webhook signature");
|
|
33
|
+
}
|
|
34
|
+
let event;
|
|
35
|
+
try {
|
|
36
|
+
event = this.stripe.webhooks.constructEvent(request.rawBody, signatureHeader, this.configuration.webhookVerificationKey);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
throw new BadRequestException("Invalid Stripe webhook signature");
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
await this.paymentEventService.receive(event);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// A non-2xx response tells Stripe to retry delivery. The event row is
|
|
46
|
+
// deliberately durable so a later delivery can enqueue it again.
|
|
47
|
+
throw new ServiceUnavailableException("Payment event could not be queued");
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
SecureStripeWebhookService = __decorate([
|
|
52
|
+
Injectable(),
|
|
53
|
+
__param(0, Inject("SecureStripeClient")),
|
|
54
|
+
__param(1, Inject(SECURE_STRIPE_CONFIGURATION)),
|
|
55
|
+
__metadata("design:paramtypes", [Stripe, Object, StripePaymentEventService])
|
|
56
|
+
], SecureStripeWebhookService);
|
|
57
|
+
export { SecureStripeWebhookService };
|
|
58
|
+
//# sourceMappingURL=secure-stripe-webhook.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secure-stripe-webhook.service.js","sourceRoot":"","sources":["../../../src/stripe-payments/services/secure-stripe-webhook.service.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,OAAO,EACH,mBAAmB,EACnB,MAAM,EACN,UAAU,EACV,2BAA2B,GAC9B,MAAM,gBAAgB,CAAC;AACxB,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EACH,2BAA2B,GAE9B,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAC,yBAAyB,EAAC,MAAM,mCAAmC,CAAC;AAQrE,IAAM,0BAA0B,GAAhC,MAAM,0BAA0B;IAGd;IAEA;IACA;IALrB,YAEqB,MAAc,EAEd,aAAwC,EACxC,mBAA8C;QAH9C,WAAM,GAAN,MAAM,CAAQ;QAEd,kBAAa,GAAb,aAAa,CAA2B;QACxC,wBAAmB,GAAnB,mBAAmB,CAA2B;IAChE,CAAC;IAEJ,KAAK,CAAC,aAAa,CAAC,OAAgC;QAChD,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;YACnB,MAAM,IAAI,mBAAmB,CACzB,4CAA4C,CAC/C,CAAC;QACN,CAAC;QACD,MAAM,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;QAC5D,IAAI,OAAO,eAAe,KAAK,QAAQ,EAAE,CAAC;YACtC,MAAM,IAAI,mBAAmB,CAAC,kCAAkC,CAAC,CAAC;QACtE,CAAC;QAED,IAAI,KAAmB,CAAC;QACxB,IAAI,CAAC;YACD,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CACvC,OAAO,CAAC,OAAO,EACf,eAAe,EACf,IAAI,CAAC,aAAa,CAAC,sBAAsB,CAC5C,CAAC;QACN,CAAC;QAAC,MAAM,CAAC;YACL,MAAM,IAAI,mBAAmB,CAAC,kCAAkC,CAAC,CAAC;QACtE,CAAC;QAED,IAAI,CAAC;YACD,MAAM,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAClD,CAAC;QAAC,MAAM,CAAC;YACL,sEAAsE;YACtE,iEAAiE;YACjE,MAAM,IAAI,2BAA2B,CACjC,mCAAmC,CACtC,CAAC;QACN,CAAC;IACL,CAAC;CACJ,CAAA;AAzCY,0BAA0B;IADtC,UAAU,EAAE;IAGJ,WAAA,MAAM,CAAC,oBAAoB,CAAC,CAAA;IAE5B,WAAA,MAAM,CAAC,2BAA2B,CAAC,CAAA;qCADX,MAAM,UAGO,yBAAyB;GAN1D,0BAA0B,CAyCtC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { OrganisationSubscriptionRecord } from "../../organisation-subscriptions/entities/organisation-subscription.entity.js";
|
|
2
|
+
import { Queue } from "bullmq";
|
|
3
|
+
import { Repository } from "typeorm";
|
|
4
|
+
import type { StripePaymentsEntitlementInput } from "../stripe-payments.extensions.js";
|
|
5
|
+
export type SecureSubscriptionInput = StripePaymentsEntitlementInput;
|
|
6
|
+
export declare class SecureSubscriptionService {
|
|
7
|
+
private readonly subscriptionRepository;
|
|
8
|
+
private readonly activationQueue;
|
|
9
|
+
private readonly logger;
|
|
10
|
+
constructor(subscriptionRepository: Repository<OrganisationSubscriptionRecord>, activationQueue: Queue);
|
|
11
|
+
findByUuid(uuid: string): Promise<OrganisationSubscriptionRecord>;
|
|
12
|
+
findByTransactionId(paymentSystemTransactionId: string): Promise<OrganisationSubscriptionRecord | null>;
|
|
13
|
+
findByCustomerId(paymentSystemCustomerId: string, mode?: string): Promise<OrganisationSubscriptionRecord[]>;
|
|
14
|
+
save(input: SecureSubscriptionInput): Promise<OrganisationSubscriptionRecord>;
|
|
15
|
+
revokeCustomerPayments(customerId: string, input: Pick<SecureSubscriptionInput, "stripeEventId" | "stripeEventCreatedAt" | "stripeStatus">): Promise<void>;
|
|
16
|
+
revokePayment(paymentSystemTransactionId: string, input: Pick<SecureSubscriptionInput, "stripeEventId" | "stripeEventCreatedAt" | "stripeStatus">): Promise<boolean>;
|
|
17
|
+
private saveRecord;
|
|
18
|
+
}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
11
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
|
+
};
|
|
13
|
+
var SecureSubscriptionService_1;
|
|
14
|
+
import { Injectable, Logger } from "@nestjs/common";
|
|
15
|
+
import { InjectQueue } from "@nestjs/bullmq";
|
|
16
|
+
import { Organisation } from "../../organisation/entities/organisation.entity.js";
|
|
17
|
+
import { OrganisationSubscriptionRecord } from "../../organisation-subscriptions/entities/organisation-subscription.entity.js";
|
|
18
|
+
import { Queue } from "bullmq";
|
|
19
|
+
import { InjectRepository } from "@nestjs/typeorm";
|
|
20
|
+
import { QueryFailedError, Repository } from "typeorm";
|
|
21
|
+
import { StripePaymentState } from "../entities/stripe-payment-state.entity.js";
|
|
22
|
+
let SecureSubscriptionService = SecureSubscriptionService_1 = class SecureSubscriptionService {
|
|
23
|
+
subscriptionRepository;
|
|
24
|
+
activationQueue;
|
|
25
|
+
logger = new Logger(SecureSubscriptionService_1.name);
|
|
26
|
+
constructor(subscriptionRepository, activationQueue) {
|
|
27
|
+
this.subscriptionRepository = subscriptionRepository;
|
|
28
|
+
this.activationQueue = activationQueue;
|
|
29
|
+
}
|
|
30
|
+
async findByUuid(uuid) {
|
|
31
|
+
const record = await this.subscriptionRepository.findOne({
|
|
32
|
+
where: { uuid },
|
|
33
|
+
relations: { organisation: true },
|
|
34
|
+
});
|
|
35
|
+
if (!record) {
|
|
36
|
+
throw new Error("Subscription record not found");
|
|
37
|
+
}
|
|
38
|
+
return record;
|
|
39
|
+
}
|
|
40
|
+
async findByTransactionId(paymentSystemTransactionId) {
|
|
41
|
+
return this.subscriptionRepository.findOne({
|
|
42
|
+
where: { paymentSystemTransactionId },
|
|
43
|
+
relations: { organisation: true },
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
async findByCustomerId(paymentSystemCustomerId, mode) {
|
|
47
|
+
return this.subscriptionRepository.find({
|
|
48
|
+
where: {
|
|
49
|
+
paymentSystemCustomerId,
|
|
50
|
+
...(mode ? { paymentSystemMode: mode } : {}),
|
|
51
|
+
},
|
|
52
|
+
relations: { organisation: true },
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
async save(input) {
|
|
56
|
+
const { record, shouldActivate } = await this.saveRecord(input);
|
|
57
|
+
if (shouldActivate) {
|
|
58
|
+
await this.activationQueue.add("activation-changed", {
|
|
59
|
+
organisationUuid: record.organisation.uuid,
|
|
60
|
+
subscriptionUuid: record.uuid,
|
|
61
|
+
productKey: record.internalSku,
|
|
62
|
+
active: record.validUntil > new Date(),
|
|
63
|
+
}, {
|
|
64
|
+
jobId: `subscription-activation:${record.uuid}:${String(record.validUntil.getTime())}`,
|
|
65
|
+
attempts: 5,
|
|
66
|
+
backoff: { type: "exponential", delay: 2_000 },
|
|
67
|
+
removeOnComplete: { age: 86_400, count: 10_000 },
|
|
68
|
+
removeOnFail: { age: 604_800, count: 10_000 },
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
return record;
|
|
72
|
+
}
|
|
73
|
+
async revokeCustomerPayments(customerId, input) {
|
|
74
|
+
const records = await this.findByCustomerId(customerId, "payment");
|
|
75
|
+
for (const record of records) {
|
|
76
|
+
await this.save({
|
|
77
|
+
paymentSystemTransactionId: record.paymentSystemTransactionId,
|
|
78
|
+
paymentSystemProductId: record.paymentSystemProductId,
|
|
79
|
+
paymentSystemCustomerId: record.paymentSystemCustomerId,
|
|
80
|
+
paymentSystemCustomerEmail: record.paymentSystemCustomerEmail,
|
|
81
|
+
paymentSystemMode: "payment",
|
|
82
|
+
paymentSystemName: "stripe",
|
|
83
|
+
validUntil: new Date(),
|
|
84
|
+
internalSku: record.internalSku,
|
|
85
|
+
productDisplayName: record.productDisplayName,
|
|
86
|
+
organisationUuid: record.organisation.uuid,
|
|
87
|
+
stripeEventId: input.stripeEventId,
|
|
88
|
+
stripeEventCreatedAt: input.stripeEventCreatedAt,
|
|
89
|
+
stripeStatus: input.stripeStatus,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async revokePayment(paymentSystemTransactionId, input) {
|
|
94
|
+
const record = await this.findByTransactionId(paymentSystemTransactionId);
|
|
95
|
+
if (record?.paymentSystemMode !== "payment") {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
await this.save({
|
|
99
|
+
paymentSystemTransactionId: record.paymentSystemTransactionId,
|
|
100
|
+
paymentSystemProductId: record.paymentSystemProductId,
|
|
101
|
+
paymentSystemCustomerId: record.paymentSystemCustomerId,
|
|
102
|
+
paymentSystemCustomerEmail: record.paymentSystemCustomerEmail,
|
|
103
|
+
paymentSystemMode: "payment",
|
|
104
|
+
paymentSystemName: "stripe",
|
|
105
|
+
validUntil: new Date(),
|
|
106
|
+
internalSku: record.internalSku,
|
|
107
|
+
productDisplayName: record.productDisplayName,
|
|
108
|
+
organisationUuid: record.organisation.uuid,
|
|
109
|
+
stripeEventId: input.stripeEventId,
|
|
110
|
+
stripeEventCreatedAt: input.stripeEventCreatedAt,
|
|
111
|
+
stripeStatus: input.stripeStatus,
|
|
112
|
+
});
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
async saveRecord(input) {
|
|
116
|
+
try {
|
|
117
|
+
return await this.subscriptionRepository.manager.transaction(async (manager) => {
|
|
118
|
+
await manager.query("SELECT pg_advisory_xact_lock(hashtext($1))", [input.paymentSystemTransactionId]);
|
|
119
|
+
const subscriptionRepository = manager.getRepository(OrganisationSubscriptionRecord);
|
|
120
|
+
const stateRepository = manager.getRepository(StripePaymentState);
|
|
121
|
+
const state = await stateRepository.findOne({
|
|
122
|
+
where: {
|
|
123
|
+
paymentSystemTransactionId: input.paymentSystemTransactionId,
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
let record = await subscriptionRepository.findOne({
|
|
127
|
+
where: {
|
|
128
|
+
paymentSystemTransactionId: input.paymentSystemTransactionId,
|
|
129
|
+
},
|
|
130
|
+
relations: { organisation: true },
|
|
131
|
+
});
|
|
132
|
+
const isDuplicateEvent = state?.lastStripeEventCreatedAt.getTime() ===
|
|
133
|
+
input.stripeEventCreatedAt.getTime() &&
|
|
134
|
+
state.lastStripeEventId === input.stripeEventId;
|
|
135
|
+
if (record &&
|
|
136
|
+
state &&
|
|
137
|
+
(state.lastStripeEventCreatedAt >
|
|
138
|
+
input.stripeEventCreatedAt ||
|
|
139
|
+
isDuplicateEvent)) {
|
|
140
|
+
return {
|
|
141
|
+
record,
|
|
142
|
+
// Retry the durable activation notification if the
|
|
143
|
+
// previous attempt failed after the DB commit.
|
|
144
|
+
shouldActivate: isDuplicateEvent,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
const isNewRecord = !record;
|
|
148
|
+
if (!record) {
|
|
149
|
+
const organisation = input.organisationUuid
|
|
150
|
+
? await manager
|
|
151
|
+
.getRepository(Organisation)
|
|
152
|
+
.findOne({
|
|
153
|
+
where: { uuid: input.organisationUuid },
|
|
154
|
+
})
|
|
155
|
+
: null;
|
|
156
|
+
if (!organisation) {
|
|
157
|
+
throw new Error("Payment could not be matched to an organisation");
|
|
158
|
+
}
|
|
159
|
+
record = subscriptionRepository.create({
|
|
160
|
+
organisation,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
const previousValidUntil = isNewRecord
|
|
164
|
+
? undefined
|
|
165
|
+
: record.validUntil.getTime();
|
|
166
|
+
record.paymentSystemTransactionId =
|
|
167
|
+
input.paymentSystemTransactionId;
|
|
168
|
+
record.paymentSystemProductId =
|
|
169
|
+
input.paymentSystemProductId;
|
|
170
|
+
record.paymentSystemCustomerId =
|
|
171
|
+
input.paymentSystemCustomerId;
|
|
172
|
+
record.paymentSystemCustomerEmail =
|
|
173
|
+
input.paymentSystemCustomerEmail;
|
|
174
|
+
record.paymentSystemMode = input.paymentSystemMode;
|
|
175
|
+
record.paymentSystemName = input.paymentSystemName;
|
|
176
|
+
record.validUntil = input.validUntil;
|
|
177
|
+
record.internalSku = input.internalSku;
|
|
178
|
+
record.productDisplayName = input.productDisplayName;
|
|
179
|
+
const savedRecord = await subscriptionRepository.save(record);
|
|
180
|
+
const shouldActivate = isNewRecord ||
|
|
181
|
+
state?.lastStripeEventId !== input.stripeEventId ||
|
|
182
|
+
previousValidUntil !== input.validUntil.getTime();
|
|
183
|
+
const paymentState = state ??
|
|
184
|
+
stateRepository.create({
|
|
185
|
+
paymentSystemTransactionId: input.paymentSystemTransactionId,
|
|
186
|
+
});
|
|
187
|
+
paymentState.lastStripeEventId = input.stripeEventId;
|
|
188
|
+
paymentState.lastStripeEventCreatedAt =
|
|
189
|
+
input.stripeEventCreatedAt;
|
|
190
|
+
paymentState.status = input.stripeStatus;
|
|
191
|
+
await stateRepository.save(paymentState);
|
|
192
|
+
return { record: savedRecord, shouldActivate };
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
if (error instanceof QueryFailedError &&
|
|
197
|
+
error
|
|
198
|
+
.driverError.code === "23505") {
|
|
199
|
+
const existing = await this.findByTransactionId(input.paymentSystemTransactionId);
|
|
200
|
+
if (existing) {
|
|
201
|
+
this.logger.warn(`Ignored concurrent duplicate payment ${input.paymentSystemTransactionId}`);
|
|
202
|
+
return { record: existing, shouldActivate: false };
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
throw error;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
SecureSubscriptionService = SecureSubscriptionService_1 = __decorate([
|
|
210
|
+
Injectable(),
|
|
211
|
+
__param(0, InjectRepository(OrganisationSubscriptionRecord)),
|
|
212
|
+
__param(1, InjectQueue("subscription-activation-changed")),
|
|
213
|
+
__metadata("design:paramtypes", [Repository,
|
|
214
|
+
Queue])
|
|
215
|
+
], SecureSubscriptionService);
|
|
216
|
+
export { SecureSubscriptionService };
|
|
217
|
+
//# sourceMappingURL=secure-subscription.service.js.map
|