@commercejs/checkout 0.1.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.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=checkout-session.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkout-session.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/checkout-session.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,357 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { CheckoutSession } from '../checkout-session.js';
3
+ // ---------------------------------------------------------------------------
4
+ // Mock helpers
5
+ // ---------------------------------------------------------------------------
6
+ function mockPaymentSession(overrides = {}) {
7
+ return {
8
+ id: 'ps_test_123',
9
+ providerId: 'mock',
10
+ status: 'pending',
11
+ amount: 99.99,
12
+ currency: 'SAR',
13
+ providerData: null,
14
+ redirectUrl: 'https://gateway.com/3ds/abc',
15
+ createdAt: '2026-02-09T12:00:00Z',
16
+ ...overrides,
17
+ };
18
+ }
19
+ function mockProvider(overrides = {}) {
20
+ return {
21
+ id: 'mock',
22
+ name: 'Mock Provider',
23
+ createSession: vi.fn().mockResolvedValue(mockPaymentSession()),
24
+ confirmSession: vi.fn().mockResolvedValue(mockPaymentSession({ status: 'captured' })),
25
+ getSession: vi.fn().mockResolvedValue(mockPaymentSession()),
26
+ refund: vi.fn().mockResolvedValue(mockPaymentSession({ status: 'refunded' })),
27
+ ...overrides,
28
+ };
29
+ }
30
+ function defaultConfig(overrides = {}) {
31
+ return {
32
+ paymentProvider: mockProvider(),
33
+ currency: 'SAR',
34
+ amount: 99.99,
35
+ returnUrl: 'https://store.com/confirm',
36
+ ...overrides,
37
+ };
38
+ }
39
+ const address = {
40
+ firstName: 'Ali',
41
+ lastName: 'Ahmed',
42
+ phone: '+966500000000',
43
+ street: '123 King Fahd Rd',
44
+ street2: null,
45
+ city: 'Riyadh',
46
+ state: 'Riyadh',
47
+ country: 'SA',
48
+ postalCode: '12345',
49
+ district: null,
50
+ nationalAddress: null,
51
+ additionalNumber: null,
52
+ };
53
+ // ---------------------------------------------------------------------------
54
+ // Tests
55
+ // ---------------------------------------------------------------------------
56
+ describe('CheckoutSession', () => {
57
+ let session;
58
+ beforeEach(() => {
59
+ session = new CheckoutSession(defaultConfig());
60
+ });
61
+ // ---- Initial state ----------------------------------------------------
62
+ describe('initial state', () => {
63
+ it('starts in idle state', () => {
64
+ expect(session.state).toBe('idle');
65
+ });
66
+ it('has correct config values', () => {
67
+ expect(session.amount).toBe(99.99);
68
+ expect(session.currency).toBe('SAR');
69
+ });
70
+ it('has null for all optional fields', () => {
71
+ expect(session.customerInfo).toBeNull();
72
+ expect(session.shippingAddress).toBeNull();
73
+ expect(session.billingAddress).toBeNull();
74
+ expect(session.shippingMethodId).toBeNull();
75
+ expect(session.paymentSession).toBeNull();
76
+ expect(session.orderId).toBeNull();
77
+ expect(session.error).toBeNull();
78
+ });
79
+ });
80
+ // ---- setCustomerInfo --------------------------------------------------
81
+ describe('setCustomerInfo', () => {
82
+ it('transitions idle → info', () => {
83
+ session.setCustomerInfo({ email: 'ali@example.com' });
84
+ expect(session.state).toBe('info');
85
+ expect(session.customerInfo?.email).toBe('ali@example.com');
86
+ });
87
+ it('throws if not in idle state', () => {
88
+ session.setCustomerInfo({ email: 'ali@example.com' });
89
+ expect(() => session.setCustomerInfo({ email: 'x@x.com' }))
90
+ .toThrow('Invalid transition');
91
+ });
92
+ });
93
+ // ---- setShippingAddress -----------------------------------------------
94
+ describe('setShippingAddress', () => {
95
+ it('transitions info → shipping', () => {
96
+ session.setCustomerInfo({ email: 'ali@example.com' });
97
+ session.setShippingAddress(address);
98
+ expect(session.state).toBe('shipping');
99
+ expect(session.shippingAddress?.city).toBe('Riyadh');
100
+ });
101
+ it('defaults billing address to shipping address', () => {
102
+ session.setCustomerInfo({ email: 'ali@example.com' });
103
+ session.setShippingAddress(address);
104
+ expect(session.billingAddress).toEqual(address);
105
+ });
106
+ it('allows separate billing address', () => {
107
+ session.setCustomerInfo({ email: 'ali@example.com' });
108
+ const billing = { ...address, city: 'Jeddah' };
109
+ session.setShippingAddress(address, billing);
110
+ expect(session.shippingAddress?.city).toBe('Riyadh');
111
+ expect(session.billingAddress?.city).toBe('Jeddah');
112
+ });
113
+ it('throws if not in info state', () => {
114
+ expect(() => session.setShippingAddress(address))
115
+ .toThrow('Invalid transition');
116
+ });
117
+ });
118
+ // ---- setShippingMethod ------------------------------------------------
119
+ describe('setShippingMethod', () => {
120
+ it('sets method ID in shipping state', () => {
121
+ session.setCustomerInfo({ email: 'ali@example.com' });
122
+ session.setShippingAddress(address);
123
+ session.setShippingMethod('express');
124
+ expect(session.shippingMethodId).toBe('express');
125
+ });
126
+ it('throws if not in shipping state', () => {
127
+ expect(() => session.setShippingMethod('express'))
128
+ .toThrow('Cannot set shipping method');
129
+ });
130
+ });
131
+ // ---- submitPayment ----------------------------------------------------
132
+ describe('submitPayment', () => {
133
+ beforeEach(() => {
134
+ session.setCustomerInfo({ email: 'ali@example.com' });
135
+ session.setShippingAddress(address);
136
+ session.setShippingMethod('standard');
137
+ });
138
+ it('transitions shipping → payment when redirect needed', async () => {
139
+ await session.submitPayment({ sourceToken: 'tok_xxx' });
140
+ expect(session.state).toBe('payment');
141
+ expect(session.paymentSession?.redirectUrl).toBe('https://gateway.com/3ds/abc');
142
+ });
143
+ it('emits paymentAction with redirectUrl', async () => {
144
+ const fn = vi.fn();
145
+ session.on('paymentAction', fn);
146
+ await session.submitPayment({ sourceToken: 'tok_xxx' });
147
+ expect(fn).toHaveBeenCalledWith({ redirectUrl: 'https://gateway.com/3ds/abc' });
148
+ });
149
+ it('goes straight to complete if already captured', async () => {
150
+ const provider = mockProvider({
151
+ createSession: vi.fn().mockResolvedValue(mockPaymentSession({ status: 'captured', redirectUrl: null })),
152
+ });
153
+ session = new CheckoutSession(defaultConfig({ paymentProvider: provider }));
154
+ session.setCustomerInfo({ email: 'ali@example.com' });
155
+ session.setShippingAddress(address);
156
+ session.setShippingMethod('standard');
157
+ const completeFn = vi.fn();
158
+ session.on('complete', completeFn);
159
+ await session.submitPayment();
160
+ expect(session.state).toBe('complete');
161
+ expect(completeFn).toHaveBeenCalled();
162
+ });
163
+ it('transitions to failed on provider error', async () => {
164
+ const provider = mockProvider({
165
+ createSession: vi.fn().mockRejectedValue(new Error('Gateway down')),
166
+ });
167
+ session = new CheckoutSession(defaultConfig({ paymentProvider: provider }));
168
+ session.setCustomerInfo({ email: 'ali@example.com' });
169
+ session.setShippingAddress(address);
170
+ session.setShippingMethod('standard');
171
+ const errorFn = vi.fn();
172
+ session.on('error', errorFn);
173
+ await expect(session.submitPayment()).rejects.toThrow('Gateway down');
174
+ expect(session.state).toBe('failed');
175
+ expect(session.error?.message).toBe('Gateway down');
176
+ expect(errorFn).toHaveBeenCalled();
177
+ });
178
+ it('throws if not in shipping or failed state', async () => {
179
+ session = new CheckoutSession(defaultConfig());
180
+ await expect(session.submitPayment()).rejects.toThrow('Cannot submit payment');
181
+ });
182
+ it('passes sourceToken and idempotencyKey to provider', async () => {
183
+ const createSession = vi.fn().mockResolvedValue(mockPaymentSession());
184
+ const provider = mockProvider({ createSession });
185
+ session = new CheckoutSession(defaultConfig({ paymentProvider: provider }));
186
+ session.setCustomerInfo({ email: 'ali@example.com' });
187
+ session.setShippingAddress(address);
188
+ session.setShippingMethod('standard');
189
+ await session.submitPayment({
190
+ sourceToken: 'tok_abc',
191
+ idempotencyKey: 'idem_123',
192
+ });
193
+ expect(createSession).toHaveBeenCalledWith(expect.objectContaining({
194
+ sourceToken: 'tok_abc',
195
+ idempotencyKey: 'idem_123',
196
+ }));
197
+ });
198
+ });
199
+ // ---- confirmPayment ---------------------------------------------------
200
+ describe('confirmPayment', () => {
201
+ beforeEach(async () => {
202
+ session.setCustomerInfo({ email: 'ali@example.com' });
203
+ session.setShippingAddress(address);
204
+ session.setShippingMethod('standard');
205
+ await session.submitPayment({ sourceToken: 'tok_xxx' });
206
+ });
207
+ it('transitions payment → confirming → complete', async () => {
208
+ const stateChanges = [];
209
+ session.on('stateChange', (e) => stateChanges.push(e));
210
+ await session.confirmPayment('ps_test_123');
211
+ expect(session.state).toBe('complete');
212
+ expect(stateChanges).toEqual([
213
+ { from: 'payment', to: 'confirming' },
214
+ { from: 'confirming', to: 'complete' },
215
+ ]);
216
+ });
217
+ it('emits complete event', async () => {
218
+ const fn = vi.fn();
219
+ session.on('complete', fn);
220
+ await session.confirmPayment();
221
+ expect(fn).toHaveBeenCalledWith({
222
+ paymentSession: expect.objectContaining({ status: 'captured' }),
223
+ });
224
+ });
225
+ it('transitions to failed if payment failed', async () => {
226
+ const provider = mockProvider({
227
+ createSession: vi.fn().mockResolvedValue(mockPaymentSession()),
228
+ confirmSession: vi.fn().mockResolvedValue(mockPaymentSession({ status: 'failed' })),
229
+ });
230
+ session = new CheckoutSession(defaultConfig({ paymentProvider: provider }));
231
+ session.setCustomerInfo({ email: 'ali@example.com' });
232
+ session.setShippingAddress(address);
233
+ session.setShippingMethod('standard');
234
+ await session.submitPayment();
235
+ await expect(session.confirmPayment()).rejects.toThrow('Payment failed');
236
+ expect(session.state).toBe('failed');
237
+ });
238
+ it('uses stored session ID if none provided', async () => {
239
+ const confirmSession = vi.fn().mockResolvedValue(mockPaymentSession({ status: 'captured' }));
240
+ const provider = mockProvider({ confirmSession });
241
+ session = new CheckoutSession(defaultConfig({ paymentProvider: provider }));
242
+ session.setCustomerInfo({ email: 'ali@example.com' });
243
+ session.setShippingAddress(address);
244
+ session.setShippingMethod('standard');
245
+ await session.submitPayment();
246
+ await session.confirmPayment();
247
+ expect(confirmSession).toHaveBeenCalledWith('ps_test_123');
248
+ });
249
+ it('throws if not in payment state', async () => {
250
+ session = new CheckoutSession(defaultConfig());
251
+ await expect(session.confirmPayment('x')).rejects.toThrow('Cannot confirm payment');
252
+ });
253
+ });
254
+ // ---- Retry flow -------------------------------------------------------
255
+ describe('retry flow (failed → payment)', () => {
256
+ it('allows re-submitting payment after failure', async () => {
257
+ const createSession = vi.fn()
258
+ .mockRejectedValueOnce(new Error('Timeout'))
259
+ .mockResolvedValueOnce(mockPaymentSession());
260
+ const provider = mockProvider({ createSession });
261
+ session = new CheckoutSession(defaultConfig({ paymentProvider: provider }));
262
+ session.setCustomerInfo({ email: 'ali@example.com' });
263
+ session.setShippingAddress(address);
264
+ session.setShippingMethod('standard');
265
+ // First attempt fails
266
+ await expect(session.submitPayment()).rejects.toThrow('Timeout');
267
+ expect(session.state).toBe('failed');
268
+ // Retry succeeds
269
+ await session.submitPayment({ sourceToken: 'tok_retry' });
270
+ expect(session.state).toBe('payment');
271
+ });
272
+ });
273
+ // ---- State change events ----------------------------------------------
274
+ describe('stateChange events', () => {
275
+ it('fires on every transition through the full flow', async () => {
276
+ const changes = [];
277
+ const provider = mockProvider({
278
+ createSession: vi.fn().mockResolvedValue(mockPaymentSession({ status: 'captured', redirectUrl: null })),
279
+ });
280
+ session = new CheckoutSession(defaultConfig({ paymentProvider: provider }));
281
+ session.on('stateChange', (e) => changes.push(e));
282
+ session.setCustomerInfo({ email: 'ali@example.com' });
283
+ session.setShippingAddress(address);
284
+ session.setShippingMethod('standard');
285
+ await session.submitPayment();
286
+ expect(changes).toEqual([
287
+ { from: 'idle', to: 'info' },
288
+ { from: 'info', to: 'shipping' },
289
+ { from: 'shipping', to: 'confirming' },
290
+ { from: 'confirming', to: 'complete' },
291
+ ]);
292
+ });
293
+ });
294
+ // ---- Setters ----------------------------------------------------------
295
+ describe('setAmount', () => {
296
+ it('updates amount before payment', () => {
297
+ session.setAmount(200);
298
+ expect(session.amount).toBe(200);
299
+ });
300
+ it('throws if in complete state', async () => {
301
+ const provider = mockProvider({
302
+ createSession: vi.fn().mockResolvedValue(mockPaymentSession({ status: 'captured', redirectUrl: null })),
303
+ });
304
+ session = new CheckoutSession(defaultConfig({ paymentProvider: provider }));
305
+ session.setCustomerInfo({ email: 'ali@example.com' });
306
+ session.setShippingAddress(address);
307
+ session.setShippingMethod('standard');
308
+ await session.submitPayment();
309
+ expect(() => session.setAmount(200)).toThrow('Cannot update amount');
310
+ });
311
+ });
312
+ describe('setOrderId', () => {
313
+ it('sets order ID', () => {
314
+ session.setOrderId('order-abc');
315
+ expect(session.orderId).toBe('order-abc');
316
+ });
317
+ });
318
+ // ---- toSnapshot -------------------------------------------------------
319
+ describe('toSnapshot', () => {
320
+ it('returns serializable snapshot', () => {
321
+ session.setCustomerInfo({ email: 'ali@example.com', firstName: 'Ali' });
322
+ const snap = session.toSnapshot();
323
+ expect(snap.state).toBe('info');
324
+ expect(snap.customerInfo).toEqual({ email: 'ali@example.com', firstName: 'Ali' });
325
+ expect(snap.amount).toBe(99.99);
326
+ expect(snap.currency).toBe('SAR');
327
+ expect(snap.error).toBeNull();
328
+ });
329
+ it('includes error message when failed', async () => {
330
+ const provider = mockProvider({
331
+ createSession: vi.fn().mockRejectedValue(new Error('Oops')),
332
+ });
333
+ session = new CheckoutSession(defaultConfig({ paymentProvider: provider }));
334
+ session.setCustomerInfo({ email: 'ali@example.com' });
335
+ session.setShippingAddress(address);
336
+ session.setShippingMethod('standard');
337
+ await session.submitPayment().catch(() => { });
338
+ const snap = session.toSnapshot();
339
+ expect(snap.state).toBe('failed');
340
+ expect(snap.error).toBe('Oops');
341
+ });
342
+ });
343
+ // ---- Invalid transitions ----------------------------------------------
344
+ describe('invalid transitions', () => {
345
+ it('cannot skip from idle to shipping', () => {
346
+ expect(() => session.setShippingAddress(address))
347
+ .toThrow('Invalid transition: "idle" → "shipping"');
348
+ });
349
+ it('cannot go backwards from shipping to info', () => {
350
+ session.setCustomerInfo({ email: 'ali@example.com' });
351
+ session.setShippingAddress(address);
352
+ expect(() => session.setCustomerInfo({ email: 'other@example.com' }))
353
+ .toThrow('Invalid transition');
354
+ });
355
+ });
356
+ });
357
+ //# sourceMappingURL=checkout-session.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkout-session.test.js","sourceRoot":"","sources":["../../src/__tests__/checkout-session.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=events.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/events.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,59 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { EventEmitter } from '../events.js';
3
+ describe('EventEmitter', () => {
4
+ // Expose emit for testing by subclassing
5
+ class TestEmitter extends EventEmitter {
6
+ fire(event, data) {
7
+ this.emit(event, data);
8
+ }
9
+ }
10
+ it('calls listener on emit', () => {
11
+ const emitter = new TestEmitter();
12
+ const fn = vi.fn();
13
+ emitter.on('ping', fn);
14
+ emitter.fire('ping', { value: 42 });
15
+ expect(fn).toHaveBeenCalledWith({ value: 42 });
16
+ });
17
+ it('supports multiple listeners', () => {
18
+ const emitter = new TestEmitter();
19
+ const fn1 = vi.fn();
20
+ const fn2 = vi.fn();
21
+ emitter.on('ping', fn1);
22
+ emitter.on('ping', fn2);
23
+ emitter.fire('ping', { value: 1 });
24
+ expect(fn1).toHaveBeenCalledOnce();
25
+ expect(fn2).toHaveBeenCalledOnce();
26
+ });
27
+ it('unsubscribes via returned function', () => {
28
+ const emitter = new TestEmitter();
29
+ const fn = vi.fn();
30
+ const unsub = emitter.on('ping', fn);
31
+ unsub();
32
+ emitter.fire('ping', { value: 1 });
33
+ expect(fn).not.toHaveBeenCalled();
34
+ });
35
+ it('once fires only once', () => {
36
+ const emitter = new TestEmitter();
37
+ const fn = vi.fn();
38
+ emitter.once('ping', fn);
39
+ emitter.fire('ping', { value: 1 });
40
+ emitter.fire('ping', { value: 2 });
41
+ expect(fn).toHaveBeenCalledOnce();
42
+ expect(fn).toHaveBeenCalledWith({ value: 1 });
43
+ });
44
+ it('removeAllListeners clears everything', () => {
45
+ const emitter = new TestEmitter();
46
+ const fn = vi.fn();
47
+ emitter.on('ping', fn);
48
+ emitter.on('pong', fn);
49
+ emitter.removeAllListeners();
50
+ emitter.fire('ping', { value: 1 });
51
+ emitter.fire('pong', { message: 'hi' });
52
+ expect(fn).not.toHaveBeenCalled();
53
+ });
54
+ it('does not throw when emitting with no listeners', () => {
55
+ const emitter = new TestEmitter();
56
+ expect(() => emitter.fire('ping', { value: 1 })).not.toThrow();
57
+ });
58
+ });
59
+ //# sourceMappingURL=events.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.test.js","sourceRoot":"","sources":["../../src/__tests__/events.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,126 @@
1
+ import type { Address, PaymentSession } from '@commercejs/types';
2
+ import { EventEmitter } from './events.js';
3
+ import type { CheckoutState, CheckoutSessionConfig, CheckoutCustomerInfo, CheckoutSnapshot, CheckoutEvents } from './types.js';
4
+ /**
5
+ * Universal checkout session — a state machine that orchestrates the
6
+ * checkout flow from customer info through payment and confirmation.
7
+ *
8
+ * Framework-agnostic: use it in a Nuxt app, a React app, a CLI, or
9
+ * directly on the server.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * import { CheckoutSession } from '@commercejs/checkout'
14
+ * import { TapPaymentProvider } from '@commercejs/payment-tap'
15
+ *
16
+ * const session = new CheckoutSession({
17
+ * paymentProvider: new TapPaymentProvider({ secretKey: 'sk_test_...' }),
18
+ * currency: 'SAR',
19
+ * amount: 199.99,
20
+ * returnUrl: 'https://checkout.mystore.com/session123/confirm',
21
+ * })
22
+ *
23
+ * // Listen for events
24
+ * session.on('stateChange', ({ from, to }) => console.log(`${from} → ${to}`))
25
+ * session.on('paymentAction', ({ redirectUrl }) => window.location.href = redirectUrl)
26
+ * session.on('complete', ({ paymentSession }) => console.log('Done!', paymentSession))
27
+ *
28
+ * // Walk through the flow
29
+ * session.setCustomerInfo({ email: 'ali@example.com', firstName: 'Ali' })
30
+ * session.setShippingAddress({ street: '...', city: 'Riyadh', ... })
31
+ * session.setShippingMethod('standard')
32
+ * await session.submitPayment({ sourceToken: 'tok_xxx' })
33
+ * // → customer redirected for 3DS
34
+ * await session.confirmPayment('chg_abc123')
35
+ * // → session.state === 'complete'
36
+ * ```
37
+ */
38
+ export declare class CheckoutSession extends EventEmitter<CheckoutEvents> {
39
+ private _state;
40
+ private _customerInfo;
41
+ private _shippingAddress;
42
+ private _billingAddress;
43
+ private _shippingMethodId;
44
+ private _paymentSession;
45
+ private _error;
46
+ private readonly provider;
47
+ private _amount;
48
+ private _currency;
49
+ private _returnUrl;
50
+ private _cancelUrl;
51
+ private _orderId;
52
+ private _webhookUrl;
53
+ constructor(config: CheckoutSessionConfig);
54
+ get state(): CheckoutState;
55
+ get customerInfo(): CheckoutCustomerInfo | null;
56
+ get shippingAddress(): Omit<Address, 'id' | 'isDefault'> | null;
57
+ get billingAddress(): Omit<Address, 'id' | 'isDefault'> | null;
58
+ get shippingMethodId(): string | null;
59
+ get paymentSession(): PaymentSession | null;
60
+ get amount(): number;
61
+ get currency(): string;
62
+ get orderId(): string | null;
63
+ get error(): Error | null;
64
+ /**
65
+ * Set customer info and transition to `info` state.
66
+ * Must be in `idle` state.
67
+ */
68
+ setCustomerInfo(info: CheckoutCustomerInfo): void;
69
+ /**
70
+ * Set shipping address and transition to `shipping` state.
71
+ * Must be in `info` state.
72
+ * Optionally set billing address (defaults to shipping address).
73
+ */
74
+ setShippingAddress(address: Omit<Address, 'id' | 'isDefault'>, billingAddress?: Omit<Address, 'id' | 'isDefault'>): void;
75
+ /**
76
+ * Set shipping method.
77
+ * Can be called while in `shipping` state (does not transition — call
78
+ * `submitPayment` when ready to move to `payment`).
79
+ */
80
+ setShippingMethod(methodId: string): void;
81
+ /**
82
+ * Update the order amount. Can be called before payment is submitted.
83
+ */
84
+ setAmount(amount: number): void;
85
+ /**
86
+ * Set order ID (if not provided at construction time).
87
+ */
88
+ setOrderId(orderId: string): void;
89
+ /**
90
+ * Submit payment — creates a payment session with the provider.
91
+ * Must be in `shipping` or `failed` (retry) state.
92
+ *
93
+ * @param options - Optional overrides for the payment session
94
+ * @returns The payment session (may include a redirectUrl for 3DS)
95
+ */
96
+ submitPayment(options?: {
97
+ sourceToken?: string;
98
+ idempotencyKey?: string;
99
+ metadata?: Record<string, unknown>;
100
+ }): Promise<PaymentSession>;
101
+ /**
102
+ * Confirm payment after 3DS redirect or async completion.
103
+ * Must be in `payment` state.
104
+ *
105
+ * @param sessionId - The payment session ID to confirm (from redirect params)
106
+ * @returns The confirmed payment session
107
+ */
108
+ confirmPayment(sessionId?: string): Promise<PaymentSession>;
109
+ /**
110
+ * Handle an async webhook update from the payment provider.
111
+ *
112
+ * Unlike `confirmPayment`, this is for trusted server-side events and
113
+ * works from any non-terminal state. It directly transitions to
114
+ * `complete` or `failed` based on the payment status.
115
+ *
116
+ * @param paymentSession - The updated payment session from the webhook
117
+ */
118
+ handleWebhookUpdate(paymentSession: PaymentSession): void;
119
+ /** Get a serializable snapshot of the current session state */
120
+ toSnapshot(): CheckoutSnapshot;
121
+ /** Transition to a new state, emit stateChange event */
122
+ private transition;
123
+ /** Assert that a transition is valid, throw if not */
124
+ private assertTransition;
125
+ }
126
+ //# sourceMappingURL=checkout-session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkout-session.d.ts","sourceRoot":"","sources":["../src/checkout-session.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,OAAO,EAAmB,cAAc,EAAE,MAAM,mBAAmB,CAAA;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,KAAK,EACV,aAAa,EACb,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EAChB,cAAc,EACf,MAAM,YAAY,CAAA;AAGnB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,qBAAa,eAAgB,SAAQ,YAAY,CAAC,cAAc,CAAC;IAE/D,OAAO,CAAC,MAAM,CAAwB;IACtC,OAAO,CAAC,aAAa,CAAoC;IACzD,OAAO,CAAC,gBAAgB,CAAiD;IACzE,OAAO,CAAC,eAAe,CAAiD;IACxE,OAAO,CAAC,iBAAiB,CAAsB;IAC/C,OAAO,CAAC,eAAe,CAA8B;IACrD,OAAO,CAAC,MAAM,CAAqB;IAGnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiB;IAC1C,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,UAAU,CAAe;IACjC,OAAO,CAAC,UAAU,CAAe;IACjC,OAAO,CAAC,QAAQ,CAAe;IAC/B,OAAO,CAAC,WAAW,CAAe;gBAEtB,MAAM,EAAE,qBAAqB;IAezC,IAAI,KAAK,IAAI,aAAa,CAAuB;IACjD,IAAI,YAAY,IAAI,oBAAoB,GAAG,IAAI,CAA8B;IAC7E,IAAI,eAAe,IAAI,IAAI,CAAC,OAAO,EAAE,IAAI,GAAG,WAAW,CAAC,GAAG,IAAI,CAAiC;IAChG,IAAI,cAAc,IAAI,IAAI,CAAC,OAAO,EAAE,IAAI,GAAG,WAAW,CAAC,GAAG,IAAI,CAAgC;IAC9F,IAAI,gBAAgB,IAAI,MAAM,GAAG,IAAI,CAAkC;IACvE,IAAI,cAAc,IAAI,cAAc,GAAG,IAAI,CAAgC;IAC3E,IAAI,MAAM,IAAI,MAAM,CAAwB;IAC5C,IAAI,QAAQ,IAAI,MAAM,CAA0B;IAChD,IAAI,OAAO,IAAI,MAAM,GAAG,IAAI,CAAyB;IACrD,IAAI,KAAK,IAAI,KAAK,GAAG,IAAI,CAAuB;IAMhD;;;OAGG;IACH,eAAe,CAAC,IAAI,EAAE,oBAAoB,GAAG,IAAI;IAMjD;;;;OAIG;IACH,kBAAkB,CAChB,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,GAAG,WAAW,CAAC,EAC1C,cAAc,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,GAAG,WAAW,CAAC,GACjD,IAAI;IAOP;;;;OAIG;IACH,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAOzC;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAO/B;;OAEG;IACH,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAQjC;;;;;;OAMG;IACG,aAAa,CAAC,OAAO,GAAE;QAC3B,WAAW,CAAC,EAAE,MAAM,CAAA;QACpB,cAAc,CAAC,EAAE,MAAM,CAAA;QACvB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAC9B,GAAG,OAAO,CAAC,cAAc,CAAC;IAqEhC;;;;;;OAMG;IACG,cAAc,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IA0CjE;;;;;;;;OAQG;IACH,mBAAmB,CAAC,cAAc,EAAE,cAAc,GAAG,IAAI;IAmCzD,+DAA+D;IAC/D,UAAU,IAAI,gBAAgB;IAmB9B,wDAAwD;IACxD,OAAO,CAAC,UAAU;IAMlB,sDAAsD;IACtD,OAAO,CAAC,gBAAgB;CASzB"}
@@ -0,0 +1,322 @@
1
+ // ---------------------------------------------------------------------------
2
+ // CheckoutSession — universal checkout state machine
3
+ // ---------------------------------------------------------------------------
4
+ //
5
+ // Pure TypeScript, zero framework deps. Works in Node, Edge, or browser.
6
+ //
7
+ // State machine:
8
+ // idle → info → shipping → payment → confirming → complete
9
+ // ↓
10
+ // failed → payment (retry)
11
+ // ---------------------------------------------------------------------------
12
+ import { EventEmitter } from './events.js';
13
+ import { CHECKOUT_TRANSITIONS } from './types.js';
14
+ /**
15
+ * Universal checkout session — a state machine that orchestrates the
16
+ * checkout flow from customer info through payment and confirmation.
17
+ *
18
+ * Framework-agnostic: use it in a Nuxt app, a React app, a CLI, or
19
+ * directly on the server.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * import { CheckoutSession } from '@commercejs/checkout'
24
+ * import { TapPaymentProvider } from '@commercejs/payment-tap'
25
+ *
26
+ * const session = new CheckoutSession({
27
+ * paymentProvider: new TapPaymentProvider({ secretKey: 'sk_test_...' }),
28
+ * currency: 'SAR',
29
+ * amount: 199.99,
30
+ * returnUrl: 'https://checkout.mystore.com/session123/confirm',
31
+ * })
32
+ *
33
+ * // Listen for events
34
+ * session.on('stateChange', ({ from, to }) => console.log(`${from} → ${to}`))
35
+ * session.on('paymentAction', ({ redirectUrl }) => window.location.href = redirectUrl)
36
+ * session.on('complete', ({ paymentSession }) => console.log('Done!', paymentSession))
37
+ *
38
+ * // Walk through the flow
39
+ * session.setCustomerInfo({ email: 'ali@example.com', firstName: 'Ali' })
40
+ * session.setShippingAddress({ street: '...', city: 'Riyadh', ... })
41
+ * session.setShippingMethod('standard')
42
+ * await session.submitPayment({ sourceToken: 'tok_xxx' })
43
+ * // → customer redirected for 3DS
44
+ * await session.confirmPayment('chg_abc123')
45
+ * // → session.state === 'complete'
46
+ * ```
47
+ */
48
+ export class CheckoutSession extends EventEmitter {
49
+ // --- Internal state ---
50
+ _state = 'idle';
51
+ _customerInfo = null;
52
+ _shippingAddress = null;
53
+ _billingAddress = null;
54
+ _shippingMethodId = null;
55
+ _paymentSession = null;
56
+ _error = null;
57
+ // --- Config ---
58
+ provider;
59
+ _amount;
60
+ _currency;
61
+ _returnUrl;
62
+ _cancelUrl;
63
+ _orderId;
64
+ _webhookUrl;
65
+ constructor(config) {
66
+ super();
67
+ this.provider = config.paymentProvider;
68
+ this._amount = config.amount;
69
+ this._currency = config.currency;
70
+ this._returnUrl = config.returnUrl ?? null;
71
+ this._cancelUrl = config.cancelUrl ?? null;
72
+ this._orderId = config.orderId ?? null;
73
+ this._webhookUrl = config.webhookUrl ?? null;
74
+ }
75
+ // ---------------------------------------------------------------------------
76
+ // Public getters (read-only)
77
+ // ---------------------------------------------------------------------------
78
+ get state() { return this._state; }
79
+ get customerInfo() { return this._customerInfo; }
80
+ get shippingAddress() { return this._shippingAddress; }
81
+ get billingAddress() { return this._billingAddress; }
82
+ get shippingMethodId() { return this._shippingMethodId; }
83
+ get paymentSession() { return this._paymentSession; }
84
+ get amount() { return this._amount; }
85
+ get currency() { return this._currency; }
86
+ get orderId() { return this._orderId; }
87
+ get error() { return this._error; }
88
+ // ---------------------------------------------------------------------------
89
+ // State transitions
90
+ // ---------------------------------------------------------------------------
91
+ /**
92
+ * Set customer info and transition to `info` state.
93
+ * Must be in `idle` state.
94
+ */
95
+ setCustomerInfo(info) {
96
+ this.assertTransition('info');
97
+ this._customerInfo = info;
98
+ this.transition('info');
99
+ }
100
+ /**
101
+ * Set shipping address and transition to `shipping` state.
102
+ * Must be in `info` state.
103
+ * Optionally set billing address (defaults to shipping address).
104
+ */
105
+ setShippingAddress(address, billingAddress) {
106
+ this.assertTransition('shipping');
107
+ this._shippingAddress = address;
108
+ this._billingAddress = billingAddress ?? address;
109
+ this.transition('shipping');
110
+ }
111
+ /**
112
+ * Set shipping method.
113
+ * Can be called while in `shipping` state (does not transition — call
114
+ * `submitPayment` when ready to move to `payment`).
115
+ */
116
+ setShippingMethod(methodId) {
117
+ if (this._state !== 'shipping') {
118
+ throw new Error(`Cannot set shipping method in "${this._state}" state`);
119
+ }
120
+ this._shippingMethodId = methodId;
121
+ }
122
+ /**
123
+ * Update the order amount. Can be called before payment is submitted.
124
+ */
125
+ setAmount(amount) {
126
+ if (this._state === 'confirming' || this._state === 'complete') {
127
+ throw new Error(`Cannot update amount in "${this._state}" state`);
128
+ }
129
+ this._amount = amount;
130
+ }
131
+ /**
132
+ * Set order ID (if not provided at construction time).
133
+ */
134
+ setOrderId(orderId) {
135
+ this._orderId = orderId;
136
+ }
137
+ // ---------------------------------------------------------------------------
138
+ // Payment flow
139
+ // ---------------------------------------------------------------------------
140
+ /**
141
+ * Submit payment — creates a payment session with the provider.
142
+ * Must be in `shipping` or `failed` (retry) state.
143
+ *
144
+ * @param options - Optional overrides for the payment session
145
+ * @returns The payment session (may include a redirectUrl for 3DS)
146
+ */
147
+ async submitPayment(options = {}) {
148
+ // Allow submission from `shipping` (normal flow) or `failed` (retry)
149
+ if (this._state !== 'shipping' && this._state !== 'failed') {
150
+ throw new Error(`Cannot submit payment in "${this._state}" state`);
151
+ }
152
+ this._error = null;
153
+ try {
154
+ const session = await this.provider.createSession({
155
+ amount: this._amount,
156
+ currency: this._currency,
157
+ sourceToken: options.sourceToken,
158
+ idempotencyKey: options.idempotencyKey,
159
+ orderId: this._orderId ?? undefined,
160
+ customerId: undefined, // future: from customerInfo lookup
161
+ customer: this._customerInfo
162
+ ? {
163
+ email: this._customerInfo.email,
164
+ firstName: this._customerInfo.firstName,
165
+ lastName: this._customerInfo.lastName,
166
+ phone: this._customerInfo.phone,
167
+ }
168
+ : undefined,
169
+ returnUrl: this._returnUrl ?? undefined,
170
+ cancelUrl: this._cancelUrl ?? undefined,
171
+ webhookUrl: this._webhookUrl ?? undefined,
172
+ metadata: options.metadata,
173
+ });
174
+ this._paymentSession = session;
175
+ // If payment is already captured (no 3DS needed), go straight to complete
176
+ if (session.status === 'captured') {
177
+ this.transition('confirming');
178
+ this.transition('complete');
179
+ this.emit('complete', { paymentSession: session });
180
+ return session;
181
+ }
182
+ // If there's a redirect URL, customer needs to complete 3DS
183
+ if (session.redirectUrl) {
184
+ this.transition('payment');
185
+ this.emit('paymentAction', { redirectUrl: session.redirectUrl });
186
+ return session;
187
+ }
188
+ // If payment was instantly declined (no 3DS), fail immediately
189
+ if (session.status === 'failed' || session.status === 'cancelled') {
190
+ const error = new Error(`Payment ${session.status}`);
191
+ this._error = error;
192
+ this.transition('failed');
193
+ this.emit('error', { error, state: this._state });
194
+ throw error;
195
+ }
196
+ // If pending/processing, wait for confirmation
197
+ this.transition('payment');
198
+ return session;
199
+ }
200
+ catch (err) {
201
+ const error = err instanceof Error ? err : new Error(String(err));
202
+ this._error = error;
203
+ this.transition('failed');
204
+ this.emit('error', { error, state: this._state });
205
+ throw error;
206
+ }
207
+ }
208
+ /**
209
+ * Confirm payment after 3DS redirect or async completion.
210
+ * Must be in `payment` state.
211
+ *
212
+ * @param sessionId - The payment session ID to confirm (from redirect params)
213
+ * @returns The confirmed payment session
214
+ */
215
+ async confirmPayment(sessionId) {
216
+ if (this._state !== 'payment') {
217
+ throw new Error(`Cannot confirm payment in "${this._state}" state`);
218
+ }
219
+ const id = sessionId ?? this._paymentSession?.id;
220
+ if (!id) {
221
+ throw new Error('No payment session ID available for confirmation');
222
+ }
223
+ this.transition('confirming');
224
+ try {
225
+ const confirmed = await this.provider.confirmSession(id);
226
+ this._paymentSession = confirmed;
227
+ if (confirmed.status === 'captured') {
228
+ this.transition('complete');
229
+ this.emit('complete', { paymentSession: confirmed });
230
+ return confirmed;
231
+ }
232
+ if (confirmed.status === 'failed' || confirmed.status === 'cancelled') {
233
+ const error = new Error(`Payment ${confirmed.status}`);
234
+ this._error = error;
235
+ this.transition('failed');
236
+ this.emit('error', { error, state: this._state });
237
+ throw error;
238
+ }
239
+ // Still processing — caller should poll or wait for webhook
240
+ return confirmed;
241
+ }
242
+ catch (err) {
243
+ const error = err instanceof Error ? err : new Error(String(err));
244
+ this._error = error;
245
+ this.transition('failed');
246
+ this.emit('error', { error, state: this._state });
247
+ throw error;
248
+ }
249
+ }
250
+ /**
251
+ * Handle an async webhook update from the payment provider.
252
+ *
253
+ * Unlike `confirmPayment`, this is for trusted server-side events and
254
+ * works from any non-terminal state. It directly transitions to
255
+ * `complete` or `failed` based on the payment status.
256
+ *
257
+ * @param paymentSession - The updated payment session from the webhook
258
+ */
259
+ handleWebhookUpdate(paymentSession) {
260
+ // Don't update terminal states
261
+ if (this._state === 'complete')
262
+ return;
263
+ // Allow re-processing failed state (idempotent)
264
+ this._paymentSession = paymentSession;
265
+ if (paymentSession.status === 'captured') {
266
+ // Skip through intermediate states directly to complete
267
+ if (this._state !== 'confirming') {
268
+ this._state = 'confirming';
269
+ }
270
+ this.transition('complete');
271
+ this.emit('complete', { paymentSession });
272
+ }
273
+ else if (paymentSession.status === 'failed' ||
274
+ paymentSession.status === 'cancelled') {
275
+ const error = new Error(`Payment ${paymentSession.status} (webhook)`);
276
+ this._error = error;
277
+ // Force to failed regardless of current state
278
+ if (this._state !== 'failed') {
279
+ this._state = 'payment'; // ensure valid transition source
280
+ this.transition('failed');
281
+ }
282
+ this.emit('error', { error, state: this._state });
283
+ }
284
+ // For pending/processing — just update the payment session data
285
+ }
286
+ // ---------------------------------------------------------------------------
287
+ // Snapshot (for serialization / SSR hydration)
288
+ // ---------------------------------------------------------------------------
289
+ /** Get a serializable snapshot of the current session state */
290
+ toSnapshot() {
291
+ return {
292
+ state: this._state,
293
+ customerInfo: this._customerInfo,
294
+ shippingAddress: this._shippingAddress,
295
+ billingAddress: this._billingAddress,
296
+ shippingMethodId: this._shippingMethodId,
297
+ paymentSession: this._paymentSession,
298
+ amount: this._amount,
299
+ currency: this._currency,
300
+ orderId: this._orderId,
301
+ error: this._error?.message ?? null,
302
+ };
303
+ }
304
+ // ---------------------------------------------------------------------------
305
+ // Internals
306
+ // ---------------------------------------------------------------------------
307
+ /** Transition to a new state, emit stateChange event */
308
+ transition(to) {
309
+ const from = this._state;
310
+ this._state = to;
311
+ this.emit('stateChange', { from, to });
312
+ }
313
+ /** Assert that a transition is valid, throw if not */
314
+ assertTransition(to) {
315
+ const allowed = CHECKOUT_TRANSITIONS[this._state];
316
+ if (!allowed.includes(to)) {
317
+ throw new Error(`Invalid transition: "${this._state}" → "${to}". ` +
318
+ `Allowed: [${allowed.join(', ')}]`);
319
+ }
320
+ }
321
+ }
322
+ //# sourceMappingURL=checkout-session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkout-session.js","sourceRoot":"","sources":["../src/checkout-session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /** Generic typed event emitter */
2
+ export declare class EventEmitter<TEvents extends {
3
+ [K in keyof TEvents]: unknown;
4
+ }> {
5
+ private listeners;
6
+ /** Subscribe to an event. Returns an unsubscribe function. */
7
+ on<K extends keyof TEvents>(event: K, fn: (data: TEvents[K]) => void): () => void;
8
+ /** Subscribe to an event, but only fire once */
9
+ once<K extends keyof TEvents>(event: K, fn: (data: TEvents[K]) => void): () => void;
10
+ /** Emit an event to all listeners */
11
+ protected emit<K extends keyof TEvents>(event: K, data: TEvents[K]): void;
12
+ /** Remove all listeners (useful for cleanup) */
13
+ removeAllListeners(): void;
14
+ }
15
+ //# sourceMappingURL=events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAIA,kCAAkC;AAClC,qBAAa,YAAY,CAAC,OAAO,SAAS;KAAG,CAAC,IAAI,MAAM,OAAO,GAAG,OAAO;CAAE;IACzE,OAAO,CAAC,SAAS,CAAuD;IAExE,8DAA8D;IAC9D,EAAE,CAAC,CAAC,SAAS,MAAM,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,MAAM,IAAI;IAYjF,gDAAgD;IAChD,IAAI,CAAC,CAAC,SAAS,MAAM,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,MAAM,IAAI;IAQnF,qCAAqC;IACrC,SAAS,CAAC,IAAI,CAAC,CAAC,SAAS,MAAM,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI;IAQzE,gDAAgD;IAChD,kBAAkB,IAAI,IAAI;CAG3B"}
package/dist/events.js ADDED
@@ -0,0 +1,40 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Tiny typed event emitter — zero dependencies, < 40 lines
3
+ // ---------------------------------------------------------------------------
4
+ /** Generic typed event emitter */
5
+ export class EventEmitter {
6
+ listeners = new Map();
7
+ /** Subscribe to an event. Returns an unsubscribe function. */
8
+ on(event, fn) {
9
+ if (!this.listeners.has(event)) {
10
+ this.listeners.set(event, new Set());
11
+ }
12
+ const set = this.listeners.get(event);
13
+ set.add(fn);
14
+ return () => {
15
+ set.delete(fn);
16
+ };
17
+ }
18
+ /** Subscribe to an event, but only fire once */
19
+ once(event, fn) {
20
+ const unsub = this.on(event, (data) => {
21
+ unsub();
22
+ fn(data);
23
+ });
24
+ return unsub;
25
+ }
26
+ /** Emit an event to all listeners */
27
+ emit(event, data) {
28
+ const set = this.listeners.get(event);
29
+ if (!set)
30
+ return;
31
+ for (const fn of set) {
32
+ fn(data);
33
+ }
34
+ }
35
+ /** Remove all listeners (useful for cleanup) */
36
+ removeAllListeners() {
37
+ this.listeners.clear();
38
+ }
39
+ }
40
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,2DAA2D;AAC3D,8EAA8E;AAE9E,kCAAkC;AAClC,MAAM,OAAO,YAAY;IACf,SAAS,GAAG,IAAI,GAAG,EAA6C,CAAA;IAExE,8DAA8D;IAC9D,EAAE,CAA0B,KAAQ,EAAE,EAA8B;QAClE,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAC/B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,GAAG,EAAE,CAAC,CAAA;QACtC,CAAC;QACD,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAE,CAAA;QACtC,GAAG,CAAC,GAAG,CAAC,EAA2B,CAAC,CAAA;QAEpC,OAAO,GAAG,EAAE;YACV,GAAG,CAAC,MAAM,CAAC,EAA2B,CAAC,CAAA;QACzC,CAAC,CAAA;IACH,CAAC;IAED,gDAAgD;IAChD,IAAI,CAA0B,KAAQ,EAAE,EAA8B;QACpE,MAAM,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE;YACpC,KAAK,EAAE,CAAA;YACP,EAAE,CAAC,IAAI,CAAC,CAAA;QACV,CAAC,CAAC,CAAA;QACF,OAAO,KAAK,CAAA;IACd,CAAC;IAED,qCAAqC;IAC3B,IAAI,CAA0B,KAAQ,EAAE,IAAgB;QAChE,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QACrC,IAAI,CAAC,GAAG;YAAE,OAAM;QAChB,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;YACpB,EAAiC,CAAC,IAAI,CAAC,CAAA;QAC1C,CAAC;IACH,CAAC;IAED,gDAAgD;IAChD,kBAAkB;QAChB,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAA;IACxB,CAAC;CACF"}
@@ -0,0 +1,5 @@
1
+ export { CheckoutSession } from './checkout-session.js';
2
+ export { EventEmitter } from './events.js';
3
+ export { CHECKOUT_TRANSITIONS } from './types.js';
4
+ export type { CheckoutState, CheckoutSessionConfig, CheckoutCustomerInfo, CheckoutSnapshot, CheckoutEvents, } from './types.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AACjD,YAAY,EACV,aAAa,EACb,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EAChB,cAAc,GACf,MAAM,YAAY,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ // ---------------------------------------------------------------------------
2
+ // @commercejs/checkout — Universal checkout engine
3
+ // ---------------------------------------------------------------------------
4
+ export { CheckoutSession } from './checkout-session.js';
5
+ export { EventEmitter } from './events.js';
6
+ export { CHECKOUT_TRANSITIONS } from './types.js';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,mDAAmD;AACnD,8EAA8E;AAE9E,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA"}
@@ -0,0 +1,64 @@
1
+ import type { Address, PaymentProvider, PaymentSession } from '@commercejs/types';
2
+ /** All possible checkout states */
3
+ export type CheckoutState = 'idle' | 'info' | 'shipping' | 'payment' | 'confirming' | 'complete' | 'failed';
4
+ /** Allowed state transitions */
5
+ export declare const CHECKOUT_TRANSITIONS: Record<CheckoutState, readonly CheckoutState[]>;
6
+ /** Customer details collected during checkout */
7
+ export interface CheckoutCustomerInfo {
8
+ email: string;
9
+ firstName?: string;
10
+ lastName?: string;
11
+ phone?: string;
12
+ }
13
+ /** Configuration for creating a CheckoutSession */
14
+ export interface CheckoutSessionConfig {
15
+ /** The payment provider to use for this session */
16
+ paymentProvider: PaymentProvider;
17
+ /** Currency code (e.g. 'SAR', 'USD') */
18
+ currency: string;
19
+ /** Total amount — can be updated before payment */
20
+ amount: number;
21
+ /** Where to return after 3DS/redirect */
22
+ returnUrl?: string;
23
+ /** Where to redirect on cancel */
24
+ cancelUrl?: string;
25
+ /** Merchant-specific order ID (if known at checkout start) */
26
+ orderId?: string;
27
+ /** Per-transaction webhook URL (e.g., Tap's post.url) */
28
+ webhookUrl?: string;
29
+ }
30
+ /** Serializable snapshot of the checkout session state */
31
+ export interface CheckoutSnapshot {
32
+ state: CheckoutState;
33
+ customerInfo: CheckoutCustomerInfo | null;
34
+ shippingAddress: Omit<Address, 'id' | 'isDefault'> | null;
35
+ billingAddress: Omit<Address, 'id' | 'isDefault'> | null;
36
+ shippingMethodId: string | null;
37
+ paymentSession: PaymentSession | null;
38
+ amount: number;
39
+ currency: string;
40
+ orderId: string | null;
41
+ error: string | null;
42
+ }
43
+ /** Events emitted by CheckoutSession */
44
+ export interface CheckoutEvents {
45
+ /** Fired on every state transition */
46
+ stateChange: {
47
+ from: CheckoutState;
48
+ to: CheckoutState;
49
+ };
50
+ /** Fired when payment requires customer action (redirect) */
51
+ paymentAction: {
52
+ redirectUrl: string;
53
+ };
54
+ /** Fired when checkout completes successfully */
55
+ complete: {
56
+ paymentSession: PaymentSession;
57
+ };
58
+ /** Fired on any error */
59
+ error: {
60
+ error: Error;
61
+ state: CheckoutState;
62
+ };
63
+ }
64
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAIjF,mCAAmC;AACnC,MAAM,MAAM,aAAa,GACrB,MAAM,GACN,MAAM,GACN,UAAU,GACV,SAAS,GACT,YAAY,GACZ,UAAU,GACV,QAAQ,CAAA;AAEZ,gCAAgC;AAChC,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,aAAa,EAAE,SAAS,aAAa,EAAE,CAQhF,CAAA;AAID,iDAAiD;AACjD,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAID,mDAAmD;AACnD,MAAM,WAAW,qBAAqB;IACpC,mDAAmD;IACnD,eAAe,EAAE,eAAe,CAAA;IAChC,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAA;IAChB,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAA;IACd,yCAAyC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,kCAAkC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,yDAAyD;IACzD,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAID,0DAA0D;AAC1D,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,aAAa,CAAA;IACpB,YAAY,EAAE,oBAAoB,GAAG,IAAI,CAAA;IACzC,eAAe,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,GAAG,WAAW,CAAC,GAAG,IAAI,CAAA;IACzD,cAAc,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,GAAG,WAAW,CAAC,GAAG,IAAI,CAAA;IACxD,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,cAAc,EAAE,cAAc,GAAG,IAAI,CAAA;IACrC,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CACrB;AAID,wCAAwC;AACxC,MAAM,WAAW,cAAc;IAC7B,sCAAsC;IACtC,WAAW,EAAE;QAAE,IAAI,EAAE,aAAa,CAAC;QAAC,EAAE,EAAE,aAAa,CAAA;KAAE,CAAA;IACvD,6DAA6D;IAC7D,aAAa,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,CAAA;IACtC,iDAAiD;IACjD,QAAQ,EAAE;QAAE,cAAc,EAAE,cAAc,CAAA;KAAE,CAAA;IAC5C,yBAAyB;IACzB,KAAK,EAAE;QAAE,KAAK,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,aAAa,CAAA;KAAE,CAAA;CAC9C"}
package/dist/types.js ADDED
@@ -0,0 +1,14 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Checkout engine types
3
+ // ---------------------------------------------------------------------------
4
+ /** Allowed state transitions */
5
+ export const CHECKOUT_TRANSITIONS = {
6
+ idle: ['info'],
7
+ info: ['shipping'],
8
+ shipping: ['payment'],
9
+ payment: ['confirming', 'failed'],
10
+ confirming: ['complete', 'failed'],
11
+ complete: [], // terminal
12
+ failed: ['payment'], // retry
13
+ };
14
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,wBAAwB;AACxB,8EAA8E;AAgB9E,gCAAgC;AAChC,MAAM,CAAC,MAAM,oBAAoB,GAAoD;IACnF,IAAI,EAAE,CAAC,MAAM,CAAC;IACd,IAAI,EAAE,CAAC,UAAU,CAAC;IAClB,QAAQ,EAAE,CAAC,SAAS,CAAC;IACrB,OAAO,EAAE,CAAC,YAAY,EAAE,QAAQ,CAAC;IACjC,UAAU,EAAE,CAAC,UAAU,EAAE,QAAQ,CAAC;IAClC,QAAQ,EAAE,EAAE,EAAS,WAAW;IAChC,MAAM,EAAE,CAAC,SAAS,CAAC,EAAE,QAAQ;CAC9B,CAAA"}
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@commercejs/checkout",
3
+ "version": "0.1.0",
4
+ "description": "Universal checkout engine — framework-agnostic state machine for payment flows",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "peerDependencies": {
20
+ "@commercejs/types": "0.1.0"
21
+ },
22
+ "devDependencies": {
23
+ "typescript": "^5.7.0",
24
+ "vitest": "^4.0.0",
25
+ "@commercejs/types": "0.1.0"
26
+ },
27
+ "scripts": {
28
+ "build": "tsc",
29
+ "typecheck": "tsc --noEmit",
30
+ "test": "vitest run",
31
+ "clean": "rm -rf dist"
32
+ }
33
+ }