@forklaunch/implementation-ecommerce-stripe 1.0.1
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/LICENSE +21 -0
- package/lib/domain/types/index.d.ts +2 -0
- package/lib/domain/types/index.d.ts.map +1 -0
- package/lib/domain/types/index.js +18 -0
- package/lib/domain/types/index.mjs +0 -0
- package/lib/domain/types/payment.mapper.types.d.ts +20 -0
- package/lib/domain/types/payment.mapper.types.d.ts.map +1 -0
- package/lib/eject/domain/types/index.ts +1 -0
- package/lib/eject/domain/types/payment.mapper.types.ts +30 -0
- package/lib/eject/services/index.ts +1 -0
- package/lib/eject/services/payment.service.ts +243 -0
- package/lib/services/index.d.ts +2 -0
- package/lib/services/index.d.ts.map +1 -0
- package/lib/services/index.js +157 -0
- package/lib/services/index.mjs +130 -0
- package/lib/services/payment.service.d.ts +90 -0
- package/lib/services/payment.service.d.ts.map +1 -0
- package/package.json +65 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 forklaunch
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../domain/types/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __copyProps = (to, from, except, desc) => {
|
|
7
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
8
|
+
for (let key of __getOwnPropNames(from))
|
|
9
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
10
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
11
|
+
}
|
|
12
|
+
return to;
|
|
13
|
+
};
|
|
14
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
15
|
+
|
|
16
|
+
// domain/types/index.ts
|
|
17
|
+
var types_exports = {};
|
|
18
|
+
module.exports = __toCommonJS(types_exports);
|
|
File without changes
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { BasePaymentDtos, BasePaymentEntities } from '@forklaunch/implementation-ecommerce-base/types';
|
|
2
|
+
import { EntityManager, InferEntity } from '@mikro-orm/core';
|
|
3
|
+
import Stripe from 'stripe';
|
|
4
|
+
/**
|
|
5
|
+
* Stripe-specific override of PaymentMappers — CreatePaymentMapper.toEntity
|
|
6
|
+
* takes the concrete Stripe.PaymentIntent (created by StripePaymentService
|
|
7
|
+
* before persistence), not the base's generic ...args: unknown[]. Same
|
|
8
|
+
* pattern as StripePlanMappers in billing overriding the generic PlanMappers.
|
|
9
|
+
*/
|
|
10
|
+
export type StripePaymentMappers<Entities extends BasePaymentEntities, Dto extends BasePaymentDtos> = {
|
|
11
|
+
PaymentMapper: {
|
|
12
|
+
entity: Entities['PaymentMapper'];
|
|
13
|
+
toDto: (entity: InferEntity<Entities['PaymentMapper']>) => Promise<Dto['PaymentMapper']>;
|
|
14
|
+
};
|
|
15
|
+
CreatePaymentMapper: {
|
|
16
|
+
entity: Entities['CreatePaymentMapper'];
|
|
17
|
+
toEntity: (dto: Dto['CreatePaymentMapper'], em: EntityManager, paymentIntent: Stripe.PaymentIntent, ...args: unknown[]) => Promise<InferEntity<Entities['CreatePaymentMapper']>>;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=payment.mapper.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payment.mapper.types.d.ts","sourceRoot":"","sources":["../../../domain/types/payment.mapper.types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,iDAAiD,CAAC;AACvG,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B;;;;;GAKG;AACH,MAAM,MAAM,oBAAoB,CAC9B,QAAQ,SAAS,mBAAmB,EACpC,GAAG,SAAS,eAAe,IACzB;IACF,aAAa,EAAE;QACb,MAAM,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC;QAClC,KAAK,EAAE,CACL,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,KAC3C,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC;KACpC,CAAC;IACF,mBAAmB,EAAE;QACnB,MAAM,EAAE,QAAQ,CAAC,qBAAqB,CAAC,CAAC;QACxC,QAAQ,EAAE,CACR,GAAG,EAAE,GAAG,CAAC,qBAAqB,CAAC,EAC/B,EAAE,EAAE,aAAa,EACjB,aAAa,EAAE,MAAM,CAAC,aAAa,EACnC,GAAG,IAAI,EAAE,OAAO,EAAE,KACf,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;KAC5D,CAAC;CACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './payment.mapper.types';
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { BasePaymentDtos, BasePaymentEntities } from '@forklaunch/implementation-ecommerce-base/types';
|
|
2
|
+
import { EntityManager, InferEntity } from '@mikro-orm/core';
|
|
3
|
+
import Stripe from 'stripe';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Stripe-specific override of PaymentMappers — CreatePaymentMapper.toEntity
|
|
7
|
+
* takes the concrete Stripe.PaymentIntent (created by StripePaymentService
|
|
8
|
+
* before persistence), not the base's generic ...args: unknown[]. Same
|
|
9
|
+
* pattern as StripePlanMappers in billing overriding the generic PlanMappers.
|
|
10
|
+
*/
|
|
11
|
+
export type StripePaymentMappers<
|
|
12
|
+
Entities extends BasePaymentEntities,
|
|
13
|
+
Dto extends BasePaymentDtos
|
|
14
|
+
> = {
|
|
15
|
+
PaymentMapper: {
|
|
16
|
+
entity: Entities['PaymentMapper'];
|
|
17
|
+
toDto: (
|
|
18
|
+
entity: InferEntity<Entities['PaymentMapper']>
|
|
19
|
+
) => Promise<Dto['PaymentMapper']>;
|
|
20
|
+
};
|
|
21
|
+
CreatePaymentMapper: {
|
|
22
|
+
entity: Entities['CreatePaymentMapper'];
|
|
23
|
+
toEntity: (
|
|
24
|
+
dto: Dto['CreatePaymentMapper'],
|
|
25
|
+
em: EntityManager,
|
|
26
|
+
paymentIntent: Stripe.PaymentIntent,
|
|
27
|
+
...args: unknown[]
|
|
28
|
+
) => Promise<InferEntity<Entities['CreatePaymentMapper']>>;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './payment.service';
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MetricsDefinition,
|
|
3
|
+
OpenTelemetryCollector,
|
|
4
|
+
TelemetryOptions
|
|
5
|
+
} from '@forklaunch/core/http';
|
|
6
|
+
import { BasePaymentService } from '@forklaunch/implementation-ecommerce-base/services';
|
|
7
|
+
import {
|
|
8
|
+
BasePaymentDtos,
|
|
9
|
+
BasePaymentEntities
|
|
10
|
+
} from '@forklaunch/implementation-ecommerce-base/types';
|
|
11
|
+
import { PaymentService } from '@forklaunch/interfaces-ecommerce/interfaces';
|
|
12
|
+
import {
|
|
13
|
+
ConfirmPaymentDto,
|
|
14
|
+
CreatePaymentDto,
|
|
15
|
+
FailPaymentDto
|
|
16
|
+
} from '@forklaunch/interfaces-ecommerce/types';
|
|
17
|
+
import { AnySchemaValidator } from '@forklaunch/validator';
|
|
18
|
+
import { EntityManager } from '@mikro-orm/core';
|
|
19
|
+
import Stripe from 'stripe';
|
|
20
|
+
import { StripePaymentMappers } from '../domain/types/payment.mapper.types';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Wraps BasePaymentService with real Stripe PaymentIntent calls — the base
|
|
24
|
+
* does DB persistence, this class calls the provider API and delegates
|
|
25
|
+
* persistence back to base (same split as StripePlanService/BasePlanService
|
|
26
|
+
* in billing). Fills the payment seam: PaymentIntent drives the order
|
|
27
|
+
* toward paid; confirmation is idempotent and driven by the webhook.
|
|
28
|
+
*/
|
|
29
|
+
export class StripePaymentService<
|
|
30
|
+
SchemaValidator extends AnySchemaValidator,
|
|
31
|
+
Entities extends BasePaymentEntities,
|
|
32
|
+
Dto extends BasePaymentDtos = BasePaymentDtos
|
|
33
|
+
> implements PaymentService
|
|
34
|
+
{
|
|
35
|
+
basePaymentService: BasePaymentService<SchemaValidator, Entities, Dto>;
|
|
36
|
+
protected readonly stripeClient: Stripe;
|
|
37
|
+
protected readonly em: EntityManager;
|
|
38
|
+
protected readonly openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>;
|
|
39
|
+
protected readonly connect?: {
|
|
40
|
+
connectedAccountId: string;
|
|
41
|
+
platformFeeBps: number;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
constructor(
|
|
45
|
+
stripeClient: Stripe,
|
|
46
|
+
em: EntityManager,
|
|
47
|
+
openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>,
|
|
48
|
+
schemaValidator: SchemaValidator,
|
|
49
|
+
mappers: StripePaymentMappers<Entities, Dto>,
|
|
50
|
+
options?: {
|
|
51
|
+
telemetry?: TelemetryOptions;
|
|
52
|
+
/**
|
|
53
|
+
* Stripe Connect (platform) mode. When set, PaymentIntents are created
|
|
54
|
+
* as DIRECT charges on the merchant's connected account — the merchant
|
|
55
|
+
* is the merchant of record and settles the funds — with
|
|
56
|
+
* `application_fee_amount` computed from platformFeeBps. The launch
|
|
57
|
+
* business model is NO markup (fee 0, see the Guild deck: "payments
|
|
58
|
+
* flat at launch"), so platformFeeBps defaults to 0 and the fee is
|
|
59
|
+
* omitted entirely; it exists as a dial, not a revenue assumption.
|
|
60
|
+
* When absent, behavior is unchanged: the deploy's own STRIPE_API_KEY
|
|
61
|
+
* account is the merchant (bring-your-own-key mode).
|
|
62
|
+
*/
|
|
63
|
+
connect?: {
|
|
64
|
+
connectedAccountId: string;
|
|
65
|
+
platformFeeBps?: number;
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
) {
|
|
69
|
+
this.stripeClient = stripeClient;
|
|
70
|
+
this.em = em;
|
|
71
|
+
this.openTelemetryCollector = openTelemetryCollector;
|
|
72
|
+
if (options?.connect) {
|
|
73
|
+
// Validate the fee dial here rather than trusting the caller: it comes
|
|
74
|
+
// from an env var, and every bad value fails SILENTLY downstream
|
|
75
|
+
// otherwise. A non-numeric STRIPE_PLATFORM_FEE_BPS makes Number()
|
|
76
|
+
// return NaN, `feeCents > 0` is then false, and the application fee is
|
|
77
|
+
// quietly omitted — an operator who meant to charge a fee would get
|
|
78
|
+
// none, with nothing logged. A negative value does the same. Failing
|
|
79
|
+
// at construction turns a silent misconfiguration into a startup error.
|
|
80
|
+
const platformFeeBps = options.connect.platformFeeBps ?? 0;
|
|
81
|
+
// Upper bound is exclusive: Stripe requires application_fee_amount to be
|
|
82
|
+
// strictly less than the charge, so 10000 bps (the whole charge) is never
|
|
83
|
+
// a usable value and is rejected here rather than at the first checkout.
|
|
84
|
+
if (
|
|
85
|
+
!Number.isInteger(platformFeeBps) ||
|
|
86
|
+
platformFeeBps < 0 ||
|
|
87
|
+
platformFeeBps >= 10000
|
|
88
|
+
) {
|
|
89
|
+
throw new Error(
|
|
90
|
+
`Invalid Stripe platform fee: expected an integer from 0 to 9999 basis points, got ${platformFeeBps}. ` +
|
|
91
|
+
'Check STRIPE_PLATFORM_FEE_BPS.'
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
this.connect = {
|
|
95
|
+
connectedAccountId: options.connect.connectedAccountId,
|
|
96
|
+
platformFeeBps
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
// CreatePaymentMapper.toEntity's 3rd param is concretely Stripe.PaymentIntent
|
|
100
|
+
// here (narrower than base's generic ...args: unknown[]) — safe at runtime,
|
|
101
|
+
// this cast just satisfies the base constructor's looser declared type.
|
|
102
|
+
this.basePaymentService = new BasePaymentService(
|
|
103
|
+
em,
|
|
104
|
+
openTelemetryCollector,
|
|
105
|
+
schemaValidator,
|
|
106
|
+
mappers as unknown as ConstructorParameters<
|
|
107
|
+
typeof BasePaymentService<SchemaValidator, Entities, Dto>
|
|
108
|
+
>[3],
|
|
109
|
+
options
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Creates the Stripe PaymentIntent, then persists the pending record with
|
|
115
|
+
* its id as providerRef. The PaymentIntent's `client_secret` is bolted
|
|
116
|
+
* onto the returned DTO (never persisted — Payment has no clientSecret
|
|
117
|
+
* column, deliberately: it's a one-time, provider-issued credential the
|
|
118
|
+
* frontend needs *right now* to actually collect the charge via
|
|
119
|
+
* Stripe.js, not something that should still be fetchable from GET
|
|
120
|
+
* /payment/:id later). This widens the return type over the
|
|
121
|
+
* PaymentService interface's `Promise<Params['PaymentDto']>` — a covariant,
|
|
122
|
+
* backward-compatible narrowing (an intersection type is always assignable
|
|
123
|
+
* to its base), the same relationship StripePaymentMappers already has to
|
|
124
|
+
* the base PaymentMappers for this class's mapper argument.
|
|
125
|
+
*/
|
|
126
|
+
async createPayment(
|
|
127
|
+
paymentDto: CreatePaymentDto,
|
|
128
|
+
em?: EntityManager
|
|
129
|
+
): Promise<Dto['PaymentMapper'] & { clientSecret?: string }> {
|
|
130
|
+
// In Connect mode this is a DIRECT charge: created on the merchant's
|
|
131
|
+
// connected account (stripeAccount request option), so the merchant is
|
|
132
|
+
// merchant-of-record and keeps the funds. The platform fee is only
|
|
133
|
+
// attached when non-zero — at launch it always is zero (no markup).
|
|
134
|
+
const requestedFeeCents = this.connect
|
|
135
|
+
? Math.round(
|
|
136
|
+
(paymentDto.amountCents * this.connect.platformFeeBps) / 10000
|
|
137
|
+
)
|
|
138
|
+
: 0;
|
|
139
|
+
// application_fee_amount has to stay strictly below the charge. The
|
|
140
|
+
// constructor already rejects 10000 bps, but rounding can still reach the
|
|
141
|
+
// full amount on a small enough charge (1 cent at 9999 bps rounds to 1),
|
|
142
|
+
// and Stripe would reject the PaymentIntent outright. Cap it so a fee
|
|
143
|
+
// setting cannot fail a customer's checkout, and record it when it bites.
|
|
144
|
+
const feeCents = Math.min(
|
|
145
|
+
requestedFeeCents,
|
|
146
|
+
Math.max(paymentDto.amountCents - 1, 0)
|
|
147
|
+
);
|
|
148
|
+
if (feeCents !== requestedFeeCents) {
|
|
149
|
+
this.openTelemetryCollector.warn(
|
|
150
|
+
'Stripe platform fee capped below the charge amount',
|
|
151
|
+
{
|
|
152
|
+
orderId: paymentDto.orderId,
|
|
153
|
+
amountCents: paymentDto.amountCents,
|
|
154
|
+
requestedFeeCents,
|
|
155
|
+
appliedFeeCents: feeCents
|
|
156
|
+
}
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
const paymentIntent = await this.stripeClient.paymentIntents.create(
|
|
160
|
+
{
|
|
161
|
+
amount: paymentDto.amountCents,
|
|
162
|
+
currency: paymentDto.currency,
|
|
163
|
+
metadata: { orderId: paymentDto.orderId },
|
|
164
|
+
...(this.connect && feeCents > 0
|
|
165
|
+
? { application_fee_amount: feeCents }
|
|
166
|
+
: {})
|
|
167
|
+
},
|
|
168
|
+
this.connect
|
|
169
|
+
? { stripeAccount: this.connect.connectedAccountId }
|
|
170
|
+
: undefined
|
|
171
|
+
);
|
|
172
|
+
const payment = await this.basePaymentService.createPayment(
|
|
173
|
+
paymentDto,
|
|
174
|
+
em ?? this.em,
|
|
175
|
+
paymentIntent
|
|
176
|
+
);
|
|
177
|
+
return {
|
|
178
|
+
...payment,
|
|
179
|
+
clientSecret: paymentIntent.client_secret ?? undefined
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Re-issues the client_secret for a payment that already has a live
|
|
185
|
+
* PaymentIntent, so a checkout retry resumes that intent instead of
|
|
186
|
+
* opening a second one against the same order. Two live intents for one
|
|
187
|
+
* order are both confirmable, and a customer who confirms both is charged
|
|
188
|
+
* twice — see PaymentOrderLookupService in ecommerce-stripe for the full
|
|
189
|
+
* failure mode.
|
|
190
|
+
*
|
|
191
|
+
* The secret has to be fetched back from Stripe because it is
|
|
192
|
+
* deliberately never persisted (see createPayment) — Stripe is the only
|
|
193
|
+
* place it exists after that first response.
|
|
194
|
+
*
|
|
195
|
+
* A payment with no providerRef never reached Stripe, so there is no
|
|
196
|
+
* intent to resume and the record is returned unchanged, without a
|
|
197
|
+
* secret. The caller treats that the same way it treats no payment at
|
|
198
|
+
* all: create a fresh one.
|
|
199
|
+
*/
|
|
200
|
+
async resumePayment(
|
|
201
|
+
idDto: { id: string },
|
|
202
|
+
em?: EntityManager
|
|
203
|
+
): Promise<Dto['PaymentMapper'] & { clientSecret?: string }> {
|
|
204
|
+
const payment = await this.basePaymentService.getPayment(idDto, em);
|
|
205
|
+
if (!payment.providerRef) {
|
|
206
|
+
return payment;
|
|
207
|
+
}
|
|
208
|
+
const paymentIntent = await this.stripeClient.paymentIntents.retrieve(
|
|
209
|
+
payment.providerRef,
|
|
210
|
+
{},
|
|
211
|
+
this.connect
|
|
212
|
+
? { stripeAccount: this.connect.connectedAccountId }
|
|
213
|
+
: undefined
|
|
214
|
+
);
|
|
215
|
+
return {
|
|
216
|
+
...payment,
|
|
217
|
+
clientSecret: paymentIntent.client_secret ?? undefined
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async getPayment(
|
|
222
|
+
idDto: { id: string },
|
|
223
|
+
em?: EntityManager
|
|
224
|
+
): Promise<Dto['PaymentMapper']> {
|
|
225
|
+
return this.basePaymentService.getPayment(idDto, em);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/** Driven by the Stripe webhook (signature-verified at the controller layer) — idempotent. */
|
|
229
|
+
async confirmPayment(
|
|
230
|
+
confirmDto: ConfirmPaymentDto,
|
|
231
|
+
em?: EntityManager
|
|
232
|
+
): Promise<Dto['PaymentMapper']> {
|
|
233
|
+
return this.basePaymentService.confirmPayment(confirmDto, em);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** Failed payment emits the event dunning hooks into, at the deployable-app layer. */
|
|
237
|
+
async failPayment(
|
|
238
|
+
failDto: FailPaymentDto,
|
|
239
|
+
em?: EntityManager
|
|
240
|
+
): Promise<Dto['PaymentMapper']> {
|
|
241
|
+
return this.basePaymentService.failPayment(failDto, em);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../services/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// services/index.ts
|
|
21
|
+
var services_exports = {};
|
|
22
|
+
__export(services_exports, {
|
|
23
|
+
StripePaymentService: () => StripePaymentService
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(services_exports);
|
|
26
|
+
|
|
27
|
+
// services/payment.service.ts
|
|
28
|
+
var import_services = require("@forklaunch/implementation-ecommerce-base/services");
|
|
29
|
+
var StripePaymentService = class {
|
|
30
|
+
basePaymentService;
|
|
31
|
+
stripeClient;
|
|
32
|
+
em;
|
|
33
|
+
openTelemetryCollector;
|
|
34
|
+
connect;
|
|
35
|
+
constructor(stripeClient, em, openTelemetryCollector, schemaValidator, mappers, options) {
|
|
36
|
+
this.stripeClient = stripeClient;
|
|
37
|
+
this.em = em;
|
|
38
|
+
this.openTelemetryCollector = openTelemetryCollector;
|
|
39
|
+
if (options?.connect) {
|
|
40
|
+
const platformFeeBps = options.connect.platformFeeBps ?? 0;
|
|
41
|
+
if (!Number.isInteger(platformFeeBps) || platformFeeBps < 0 || platformFeeBps >= 1e4) {
|
|
42
|
+
throw new Error(
|
|
43
|
+
`Invalid Stripe platform fee: expected an integer from 0 to 9999 basis points, got ${platformFeeBps}. Check STRIPE_PLATFORM_FEE_BPS.`
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
this.connect = {
|
|
47
|
+
connectedAccountId: options.connect.connectedAccountId,
|
|
48
|
+
platformFeeBps
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
this.basePaymentService = new import_services.BasePaymentService(
|
|
52
|
+
em,
|
|
53
|
+
openTelemetryCollector,
|
|
54
|
+
schemaValidator,
|
|
55
|
+
mappers,
|
|
56
|
+
options
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Creates the Stripe PaymentIntent, then persists the pending record with
|
|
61
|
+
* its id as providerRef. The PaymentIntent's `client_secret` is bolted
|
|
62
|
+
* onto the returned DTO (never persisted — Payment has no clientSecret
|
|
63
|
+
* column, deliberately: it's a one-time, provider-issued credential the
|
|
64
|
+
* frontend needs *right now* to actually collect the charge via
|
|
65
|
+
* Stripe.js, not something that should still be fetchable from GET
|
|
66
|
+
* /payment/:id later). This widens the return type over the
|
|
67
|
+
* PaymentService interface's `Promise<Params['PaymentDto']>` — a covariant,
|
|
68
|
+
* backward-compatible narrowing (an intersection type is always assignable
|
|
69
|
+
* to its base), the same relationship StripePaymentMappers already has to
|
|
70
|
+
* the base PaymentMappers for this class's mapper argument.
|
|
71
|
+
*/
|
|
72
|
+
async createPayment(paymentDto, em) {
|
|
73
|
+
const requestedFeeCents = this.connect ? Math.round(
|
|
74
|
+
paymentDto.amountCents * this.connect.platformFeeBps / 1e4
|
|
75
|
+
) : 0;
|
|
76
|
+
const feeCents = Math.min(
|
|
77
|
+
requestedFeeCents,
|
|
78
|
+
Math.max(paymentDto.amountCents - 1, 0)
|
|
79
|
+
);
|
|
80
|
+
if (feeCents !== requestedFeeCents) {
|
|
81
|
+
this.openTelemetryCollector.warn(
|
|
82
|
+
"Stripe platform fee capped below the charge amount",
|
|
83
|
+
{
|
|
84
|
+
orderId: paymentDto.orderId,
|
|
85
|
+
amountCents: paymentDto.amountCents,
|
|
86
|
+
requestedFeeCents,
|
|
87
|
+
appliedFeeCents: feeCents
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
const paymentIntent = await this.stripeClient.paymentIntents.create(
|
|
92
|
+
{
|
|
93
|
+
amount: paymentDto.amountCents,
|
|
94
|
+
currency: paymentDto.currency,
|
|
95
|
+
metadata: { orderId: paymentDto.orderId },
|
|
96
|
+
...this.connect && feeCents > 0 ? { application_fee_amount: feeCents } : {}
|
|
97
|
+
},
|
|
98
|
+
this.connect ? { stripeAccount: this.connect.connectedAccountId } : void 0
|
|
99
|
+
);
|
|
100
|
+
const payment = await this.basePaymentService.createPayment(
|
|
101
|
+
paymentDto,
|
|
102
|
+
em ?? this.em,
|
|
103
|
+
paymentIntent
|
|
104
|
+
);
|
|
105
|
+
return {
|
|
106
|
+
...payment,
|
|
107
|
+
clientSecret: paymentIntent.client_secret ?? void 0
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Re-issues the client_secret for a payment that already has a live
|
|
112
|
+
* PaymentIntent, so a checkout retry resumes that intent instead of
|
|
113
|
+
* opening a second one against the same order. Two live intents for one
|
|
114
|
+
* order are both confirmable, and a customer who confirms both is charged
|
|
115
|
+
* twice — see PaymentOrderLookupService in ecommerce-stripe for the full
|
|
116
|
+
* failure mode.
|
|
117
|
+
*
|
|
118
|
+
* The secret has to be fetched back from Stripe because it is
|
|
119
|
+
* deliberately never persisted (see createPayment) — Stripe is the only
|
|
120
|
+
* place it exists after that first response.
|
|
121
|
+
*
|
|
122
|
+
* A payment with no providerRef never reached Stripe, so there is no
|
|
123
|
+
* intent to resume and the record is returned unchanged, without a
|
|
124
|
+
* secret. The caller treats that the same way it treats no payment at
|
|
125
|
+
* all: create a fresh one.
|
|
126
|
+
*/
|
|
127
|
+
async resumePayment(idDto, em) {
|
|
128
|
+
const payment = await this.basePaymentService.getPayment(idDto, em);
|
|
129
|
+
if (!payment.providerRef) {
|
|
130
|
+
return payment;
|
|
131
|
+
}
|
|
132
|
+
const paymentIntent = await this.stripeClient.paymentIntents.retrieve(
|
|
133
|
+
payment.providerRef,
|
|
134
|
+
{},
|
|
135
|
+
this.connect ? { stripeAccount: this.connect.connectedAccountId } : void 0
|
|
136
|
+
);
|
|
137
|
+
return {
|
|
138
|
+
...payment,
|
|
139
|
+
clientSecret: paymentIntent.client_secret ?? void 0
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
async getPayment(idDto, em) {
|
|
143
|
+
return this.basePaymentService.getPayment(idDto, em);
|
|
144
|
+
}
|
|
145
|
+
/** Driven by the Stripe webhook (signature-verified at the controller layer) — idempotent. */
|
|
146
|
+
async confirmPayment(confirmDto, em) {
|
|
147
|
+
return this.basePaymentService.confirmPayment(confirmDto, em);
|
|
148
|
+
}
|
|
149
|
+
/** Failed payment emits the event dunning hooks into, at the deployable-app layer. */
|
|
150
|
+
async failPayment(failDto, em) {
|
|
151
|
+
return this.basePaymentService.failPayment(failDto, em);
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
155
|
+
0 && (module.exports = {
|
|
156
|
+
StripePaymentService
|
|
157
|
+
});
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// services/payment.service.ts
|
|
2
|
+
import { BasePaymentService } from "@forklaunch/implementation-ecommerce-base/services";
|
|
3
|
+
var StripePaymentService = class {
|
|
4
|
+
basePaymentService;
|
|
5
|
+
stripeClient;
|
|
6
|
+
em;
|
|
7
|
+
openTelemetryCollector;
|
|
8
|
+
connect;
|
|
9
|
+
constructor(stripeClient, em, openTelemetryCollector, schemaValidator, mappers, options) {
|
|
10
|
+
this.stripeClient = stripeClient;
|
|
11
|
+
this.em = em;
|
|
12
|
+
this.openTelemetryCollector = openTelemetryCollector;
|
|
13
|
+
if (options?.connect) {
|
|
14
|
+
const platformFeeBps = options.connect.platformFeeBps ?? 0;
|
|
15
|
+
if (!Number.isInteger(platformFeeBps) || platformFeeBps < 0 || platformFeeBps >= 1e4) {
|
|
16
|
+
throw new Error(
|
|
17
|
+
`Invalid Stripe platform fee: expected an integer from 0 to 9999 basis points, got ${platformFeeBps}. Check STRIPE_PLATFORM_FEE_BPS.`
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
this.connect = {
|
|
21
|
+
connectedAccountId: options.connect.connectedAccountId,
|
|
22
|
+
platformFeeBps
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
this.basePaymentService = new BasePaymentService(
|
|
26
|
+
em,
|
|
27
|
+
openTelemetryCollector,
|
|
28
|
+
schemaValidator,
|
|
29
|
+
mappers,
|
|
30
|
+
options
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Creates the Stripe PaymentIntent, then persists the pending record with
|
|
35
|
+
* its id as providerRef. The PaymentIntent's `client_secret` is bolted
|
|
36
|
+
* onto the returned DTO (never persisted — Payment has no clientSecret
|
|
37
|
+
* column, deliberately: it's a one-time, provider-issued credential the
|
|
38
|
+
* frontend needs *right now* to actually collect the charge via
|
|
39
|
+
* Stripe.js, not something that should still be fetchable from GET
|
|
40
|
+
* /payment/:id later). This widens the return type over the
|
|
41
|
+
* PaymentService interface's `Promise<Params['PaymentDto']>` — a covariant,
|
|
42
|
+
* backward-compatible narrowing (an intersection type is always assignable
|
|
43
|
+
* to its base), the same relationship StripePaymentMappers already has to
|
|
44
|
+
* the base PaymentMappers for this class's mapper argument.
|
|
45
|
+
*/
|
|
46
|
+
async createPayment(paymentDto, em) {
|
|
47
|
+
const requestedFeeCents = this.connect ? Math.round(
|
|
48
|
+
paymentDto.amountCents * this.connect.platformFeeBps / 1e4
|
|
49
|
+
) : 0;
|
|
50
|
+
const feeCents = Math.min(
|
|
51
|
+
requestedFeeCents,
|
|
52
|
+
Math.max(paymentDto.amountCents - 1, 0)
|
|
53
|
+
);
|
|
54
|
+
if (feeCents !== requestedFeeCents) {
|
|
55
|
+
this.openTelemetryCollector.warn(
|
|
56
|
+
"Stripe platform fee capped below the charge amount",
|
|
57
|
+
{
|
|
58
|
+
orderId: paymentDto.orderId,
|
|
59
|
+
amountCents: paymentDto.amountCents,
|
|
60
|
+
requestedFeeCents,
|
|
61
|
+
appliedFeeCents: feeCents
|
|
62
|
+
}
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
const paymentIntent = await this.stripeClient.paymentIntents.create(
|
|
66
|
+
{
|
|
67
|
+
amount: paymentDto.amountCents,
|
|
68
|
+
currency: paymentDto.currency,
|
|
69
|
+
metadata: { orderId: paymentDto.orderId },
|
|
70
|
+
...this.connect && feeCents > 0 ? { application_fee_amount: feeCents } : {}
|
|
71
|
+
},
|
|
72
|
+
this.connect ? { stripeAccount: this.connect.connectedAccountId } : void 0
|
|
73
|
+
);
|
|
74
|
+
const payment = await this.basePaymentService.createPayment(
|
|
75
|
+
paymentDto,
|
|
76
|
+
em ?? this.em,
|
|
77
|
+
paymentIntent
|
|
78
|
+
);
|
|
79
|
+
return {
|
|
80
|
+
...payment,
|
|
81
|
+
clientSecret: paymentIntent.client_secret ?? void 0
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Re-issues the client_secret for a payment that already has a live
|
|
86
|
+
* PaymentIntent, so a checkout retry resumes that intent instead of
|
|
87
|
+
* opening a second one against the same order. Two live intents for one
|
|
88
|
+
* order are both confirmable, and a customer who confirms both is charged
|
|
89
|
+
* twice — see PaymentOrderLookupService in ecommerce-stripe for the full
|
|
90
|
+
* failure mode.
|
|
91
|
+
*
|
|
92
|
+
* The secret has to be fetched back from Stripe because it is
|
|
93
|
+
* deliberately never persisted (see createPayment) — Stripe is the only
|
|
94
|
+
* place it exists after that first response.
|
|
95
|
+
*
|
|
96
|
+
* A payment with no providerRef never reached Stripe, so there is no
|
|
97
|
+
* intent to resume and the record is returned unchanged, without a
|
|
98
|
+
* secret. The caller treats that the same way it treats no payment at
|
|
99
|
+
* all: create a fresh one.
|
|
100
|
+
*/
|
|
101
|
+
async resumePayment(idDto, em) {
|
|
102
|
+
const payment = await this.basePaymentService.getPayment(idDto, em);
|
|
103
|
+
if (!payment.providerRef) {
|
|
104
|
+
return payment;
|
|
105
|
+
}
|
|
106
|
+
const paymentIntent = await this.stripeClient.paymentIntents.retrieve(
|
|
107
|
+
payment.providerRef,
|
|
108
|
+
{},
|
|
109
|
+
this.connect ? { stripeAccount: this.connect.connectedAccountId } : void 0
|
|
110
|
+
);
|
|
111
|
+
return {
|
|
112
|
+
...payment,
|
|
113
|
+
clientSecret: paymentIntent.client_secret ?? void 0
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
async getPayment(idDto, em) {
|
|
117
|
+
return this.basePaymentService.getPayment(idDto, em);
|
|
118
|
+
}
|
|
119
|
+
/** Driven by the Stripe webhook (signature-verified at the controller layer) — idempotent. */
|
|
120
|
+
async confirmPayment(confirmDto, em) {
|
|
121
|
+
return this.basePaymentService.confirmPayment(confirmDto, em);
|
|
122
|
+
}
|
|
123
|
+
/** Failed payment emits the event dunning hooks into, at the deployable-app layer. */
|
|
124
|
+
async failPayment(failDto, em) {
|
|
125
|
+
return this.basePaymentService.failPayment(failDto, em);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
export {
|
|
129
|
+
StripePaymentService
|
|
130
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { MetricsDefinition, OpenTelemetryCollector, TelemetryOptions } from '@forklaunch/core/http';
|
|
2
|
+
import { BasePaymentService } from '@forklaunch/implementation-ecommerce-base/services';
|
|
3
|
+
import { BasePaymentDtos, BasePaymentEntities } from '@forklaunch/implementation-ecommerce-base/types';
|
|
4
|
+
import { PaymentService } from '@forklaunch/interfaces-ecommerce/interfaces';
|
|
5
|
+
import { ConfirmPaymentDto, CreatePaymentDto, FailPaymentDto } from '@forklaunch/interfaces-ecommerce/types';
|
|
6
|
+
import { AnySchemaValidator } from '@forklaunch/validator';
|
|
7
|
+
import { EntityManager } from '@mikro-orm/core';
|
|
8
|
+
import Stripe from 'stripe';
|
|
9
|
+
import { StripePaymentMappers } from '../domain/types/payment.mapper.types';
|
|
10
|
+
/**
|
|
11
|
+
* Wraps BasePaymentService with real Stripe PaymentIntent calls — the base
|
|
12
|
+
* does DB persistence, this class calls the provider API and delegates
|
|
13
|
+
* persistence back to base (same split as StripePlanService/BasePlanService
|
|
14
|
+
* in billing). Fills the payment seam: PaymentIntent drives the order
|
|
15
|
+
* toward paid; confirmation is idempotent and driven by the webhook.
|
|
16
|
+
*/
|
|
17
|
+
export declare class StripePaymentService<SchemaValidator extends AnySchemaValidator, Entities extends BasePaymentEntities, Dto extends BasePaymentDtos = BasePaymentDtos> implements PaymentService {
|
|
18
|
+
basePaymentService: BasePaymentService<SchemaValidator, Entities, Dto>;
|
|
19
|
+
protected readonly stripeClient: Stripe;
|
|
20
|
+
protected readonly em: EntityManager;
|
|
21
|
+
protected readonly openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>;
|
|
22
|
+
protected readonly connect?: {
|
|
23
|
+
connectedAccountId: string;
|
|
24
|
+
platformFeeBps: number;
|
|
25
|
+
};
|
|
26
|
+
constructor(stripeClient: Stripe, em: EntityManager, openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>, schemaValidator: SchemaValidator, mappers: StripePaymentMappers<Entities, Dto>, options?: {
|
|
27
|
+
telemetry?: TelemetryOptions;
|
|
28
|
+
/**
|
|
29
|
+
* Stripe Connect (platform) mode. When set, PaymentIntents are created
|
|
30
|
+
* as DIRECT charges on the merchant's connected account — the merchant
|
|
31
|
+
* is the merchant of record and settles the funds — with
|
|
32
|
+
* `application_fee_amount` computed from platformFeeBps. The launch
|
|
33
|
+
* business model is NO markup (fee 0, see the Guild deck: "payments
|
|
34
|
+
* flat at launch"), so platformFeeBps defaults to 0 and the fee is
|
|
35
|
+
* omitted entirely; it exists as a dial, not a revenue assumption.
|
|
36
|
+
* When absent, behavior is unchanged: the deploy's own STRIPE_API_KEY
|
|
37
|
+
* account is the merchant (bring-your-own-key mode).
|
|
38
|
+
*/
|
|
39
|
+
connect?: {
|
|
40
|
+
connectedAccountId: string;
|
|
41
|
+
platformFeeBps?: number;
|
|
42
|
+
};
|
|
43
|
+
});
|
|
44
|
+
/**
|
|
45
|
+
* Creates the Stripe PaymentIntent, then persists the pending record with
|
|
46
|
+
* its id as providerRef. The PaymentIntent's `client_secret` is bolted
|
|
47
|
+
* onto the returned DTO (never persisted — Payment has no clientSecret
|
|
48
|
+
* column, deliberately: it's a one-time, provider-issued credential the
|
|
49
|
+
* frontend needs *right now* to actually collect the charge via
|
|
50
|
+
* Stripe.js, not something that should still be fetchable from GET
|
|
51
|
+
* /payment/:id later). This widens the return type over the
|
|
52
|
+
* PaymentService interface's `Promise<Params['PaymentDto']>` — a covariant,
|
|
53
|
+
* backward-compatible narrowing (an intersection type is always assignable
|
|
54
|
+
* to its base), the same relationship StripePaymentMappers already has to
|
|
55
|
+
* the base PaymentMappers for this class's mapper argument.
|
|
56
|
+
*/
|
|
57
|
+
createPayment(paymentDto: CreatePaymentDto, em?: EntityManager): Promise<Dto['PaymentMapper'] & {
|
|
58
|
+
clientSecret?: string;
|
|
59
|
+
}>;
|
|
60
|
+
/**
|
|
61
|
+
* Re-issues the client_secret for a payment that already has a live
|
|
62
|
+
* PaymentIntent, so a checkout retry resumes that intent instead of
|
|
63
|
+
* opening a second one against the same order. Two live intents for one
|
|
64
|
+
* order are both confirmable, and a customer who confirms both is charged
|
|
65
|
+
* twice — see PaymentOrderLookupService in ecommerce-stripe for the full
|
|
66
|
+
* failure mode.
|
|
67
|
+
*
|
|
68
|
+
* The secret has to be fetched back from Stripe because it is
|
|
69
|
+
* deliberately never persisted (see createPayment) — Stripe is the only
|
|
70
|
+
* place it exists after that first response.
|
|
71
|
+
*
|
|
72
|
+
* A payment with no providerRef never reached Stripe, so there is no
|
|
73
|
+
* intent to resume and the record is returned unchanged, without a
|
|
74
|
+
* secret. The caller treats that the same way it treats no payment at
|
|
75
|
+
* all: create a fresh one.
|
|
76
|
+
*/
|
|
77
|
+
resumePayment(idDto: {
|
|
78
|
+
id: string;
|
|
79
|
+
}, em?: EntityManager): Promise<Dto['PaymentMapper'] & {
|
|
80
|
+
clientSecret?: string;
|
|
81
|
+
}>;
|
|
82
|
+
getPayment(idDto: {
|
|
83
|
+
id: string;
|
|
84
|
+
}, em?: EntityManager): Promise<Dto['PaymentMapper']>;
|
|
85
|
+
/** Driven by the Stripe webhook (signature-verified at the controller layer) — idempotent. */
|
|
86
|
+
confirmPayment(confirmDto: ConfirmPaymentDto, em?: EntityManager): Promise<Dto['PaymentMapper']>;
|
|
87
|
+
/** Failed payment emits the event dunning hooks into, at the deployable-app layer. */
|
|
88
|
+
failPayment(failDto: FailPaymentDto, em?: EntityManager): Promise<Dto['PaymentMapper']>;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=payment.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payment.service.d.ts","sourceRoot":"","sources":["../../services/payment.service.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,gBAAgB,EACjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,kBAAkB,EAAE,MAAM,oDAAoD,CAAC;AACxF,OAAO,EACL,eAAe,EACf,mBAAmB,EACpB,MAAM,iDAAiD,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,6CAA6C,CAAC;AAC7E,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,cAAc,EACf,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AAE5E;;;;;;GAMG;AACH,qBAAa,oBAAoB,CAC/B,eAAe,SAAS,kBAAkB,EAC1C,QAAQ,SAAS,mBAAmB,EACpC,GAAG,SAAS,eAAe,GAAG,eAAe,CAC7C,YAAW,cAAc;IAEzB,kBAAkB,EAAE,kBAAkB,CAAC,eAAe,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC;IACvE,SAAS,CAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IACxC,SAAS,CAAC,QAAQ,CAAC,EAAE,EAAE,aAAa,CAAC;IACrC,SAAS,CAAC,QAAQ,CAAC,sBAAsB,EAAE,sBAAsB,CAAC,iBAAiB,CAAC,CAAC;IACrF,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE;QAC3B,kBAAkB,EAAE,MAAM,CAAC;QAC3B,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC;IAEF,YACE,YAAY,EAAE,MAAM,EACpB,EAAE,EAAE,aAAa,EACjB,sBAAsB,EAAE,sBAAsB,CAAC,iBAAiB,CAAC,EACjE,eAAe,EAAE,eAAe,EAChC,OAAO,EAAE,oBAAoB,CAAC,QAAQ,EAAE,GAAG,CAAC,EAC5C,OAAO,CAAC,EAAE;QACR,SAAS,CAAC,EAAE,gBAAgB,CAAC;QAC7B;;;;;;;;;;WAUG;QACH,OAAO,CAAC,EAAE;YACR,kBAAkB,EAAE,MAAM,CAAC;YAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;SACzB,CAAC;KACH,EA4CF;IAED;;;;;;;;;;;;OAYG;IACG,aAAa,CACjB,UAAU,EAAE,gBAAgB,EAC5B,EAAE,CAAC,EAAE,aAAa,GACjB,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,GAAG;QAAE,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAoD3D;IAED;;;;;;;;;;;;;;;;OAgBG;IACG,aAAa,CACjB,KAAK,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,EACrB,EAAE,CAAC,EAAE,aAAa,GACjB,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,GAAG;QAAE,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAgB3D;IAEK,UAAU,CACd,KAAK,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,EACrB,EAAE,CAAC,EAAE,aAAa,GACjB,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAE/B;IAED,8FAA8F;IACxF,cAAc,CAClB,UAAU,EAAE,iBAAiB,EAC7B,EAAE,CAAC,EAAE,aAAa,GACjB,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAE/B;IAED,sFAAsF;IAChF,WAAW,CACf,OAAO,EAAE,cAAc,EACvB,EAAE,CAAC,EAAE,aAAa,GACjB,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAE/B;CACF"}
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@forklaunch/implementation-ecommerce-stripe",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Stripe implementation for forklaunch ecommerce",
|
|
5
|
+
"homepage": "https://github.com/forklaunch/forklaunch-js#readme",
|
|
6
|
+
"bugs": {
|
|
7
|
+
"url": "https://github.com/forklaunch/forklaunch-js/issues"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/forklaunch/forklaunch-js.git"
|
|
12
|
+
},
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"author": "Forklift Technologies, Inc.",
|
|
15
|
+
"exports": {
|
|
16
|
+
"./services": {
|
|
17
|
+
"types": "./lib/services/index.d.ts",
|
|
18
|
+
"import": "./lib/services/index.mjs",
|
|
19
|
+
"require": "./lib/services/index.js",
|
|
20
|
+
"default": "./lib/services/index.js"
|
|
21
|
+
},
|
|
22
|
+
"./types": {
|
|
23
|
+
"types": "./lib/domain/types/index.d.ts",
|
|
24
|
+
"import": "./lib/domain/types/index.mjs",
|
|
25
|
+
"require": "./lib/domain/types/index.js",
|
|
26
|
+
"default": "./lib/domain/types/index.js"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"lib/**"
|
|
31
|
+
],
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@forklaunch/common": "^1.2.24",
|
|
34
|
+
"@forklaunch/core": "^1.5.16",
|
|
35
|
+
"@forklaunch/internal": "^1.2.26",
|
|
36
|
+
"@forklaunch/validator": "^1.2.25",
|
|
37
|
+
"@mikro-orm/core": "7.1.14",
|
|
38
|
+
"@sinclair/typebox": "^0.34.52",
|
|
39
|
+
"ajv": "^8.20.0",
|
|
40
|
+
"stripe": "^22.5.0",
|
|
41
|
+
"zod": "^4.4.3",
|
|
42
|
+
"@forklaunch/implementation-ecommerce-base": "^1.0.6",
|
|
43
|
+
"@forklaunch/interfaces-ecommerce": "^1.0.6"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@typescript/native-preview": "7.0.0-dev.20260707.2",
|
|
47
|
+
"depcheck": "^1.4.7",
|
|
48
|
+
"prettier": "^3.9.6",
|
|
49
|
+
"typedoc": "^0.28.20"
|
|
50
|
+
},
|
|
51
|
+
"publishConfig": {
|
|
52
|
+
"access": "public"
|
|
53
|
+
},
|
|
54
|
+
"scripts": {
|
|
55
|
+
"build": "tsgo --noEmit && tsup services/index.ts domain/types/index.ts --format cjs,esm --no-splitting --tsconfig tsconfig.json --out-dir lib --clean && tsgo -p tsconfig.build.json --emitDeclarationOnly && if [ -f eject-package.bash ]; then pnpm package:eject; fi",
|
|
56
|
+
"clean": "rm -rf lib pnpm.lock.yaml node_modules",
|
|
57
|
+
"docs": "typedoc --out docs *",
|
|
58
|
+
"format": "prettier --ignore-path=.prettierignore --config .prettierrc '**/*.{ts,tsx,json}' --write",
|
|
59
|
+
"lint": "eslint . -c eslint.config.mjs",
|
|
60
|
+
"lint:fix": "eslint . -c eslint.config.mjs --fix",
|
|
61
|
+
"package:eject": "./eject-package.bash",
|
|
62
|
+
"publish:package": "./publish-package.bash",
|
|
63
|
+
"test": "vitest --passWithNoTests"
|
|
64
|
+
}
|
|
65
|
+
}
|