@tineon/t9n 0.1.7 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -6,10 +6,12 @@ export declare class T9nCheckout {
6
6
  private sessionId;
7
7
  private intervalId?;
8
8
  private statusPollId?;
9
+ private closeTimeoutId?;
9
10
  private expiresAt?;
10
11
  private isOpen;
11
12
  private lastResultFailed;
12
13
  private hasAttemptedConfirm;
14
+ private skipCloseMark;
13
15
  private successNotified;
14
16
  private failureNotified;
15
17
  constructor(config: CheckoutConfig);
@@ -29,6 +31,7 @@ export declare class T9nCheckout {
29
31
  private assertSecureConfig;
30
32
  private applyButtonStyle;
31
33
  private applyButtonTheme;
34
+ private scheduleAutoClose;
32
35
  private normalizeStatus;
33
36
  private markFailed;
34
37
  private markClosed;
package/dist/index.js CHANGED
@@ -8,6 +8,7 @@ export class T9nCheckout {
8
8
  this.isOpen = false;
9
9
  this.lastResultFailed = false;
10
10
  this.hasAttemptedConfirm = false;
11
+ this.skipCloseMark = false;
11
12
  this.successNotified = false;
12
13
  this.failureNotified = false;
13
14
  this.cfg = checkoutConfigSchema.parse(config);
@@ -73,16 +74,23 @@ export class T9nCheckout {
73
74
  }
74
75
  }
75
76
  close() {
77
+ if (this.closeTimeoutId) {
78
+ window.clearTimeout(this.closeTimeoutId);
79
+ this.closeTimeoutId = undefined;
80
+ }
76
81
  if (this.intervalId)
77
82
  window.clearInterval(this.intervalId);
78
83
  if (this.statusPollId)
79
84
  window.clearInterval(this.statusPollId);
80
- if (this.lastResultFailed && this.sessionId) {
81
- void this.markFailed();
82
- }
83
- else if (!this.hasAttemptedConfirm && this.sessionId) {
84
- void this.markClosed();
85
+ if (!this.skipCloseMark) {
86
+ if (this.lastResultFailed && this.sessionId) {
87
+ void this.markFailed();
88
+ }
89
+ else if (!this.hasAttemptedConfirm && this.sessionId) {
90
+ void this.markClosed();
91
+ }
85
92
  }
93
+ this.skipCloseMark = false;
86
94
  this.modal?.close();
87
95
  this.modal = undefined;
88
96
  this.sessionId = "";
@@ -177,48 +185,95 @@ export class T9nCheckout {
177
185
  }
178
186
  async confirmPayment() {
179
187
  this.hasAttemptedConfirm = true;
188
+ console.log("[T9N] confirmPayment: start", { sessionId: this.sessionId });
180
189
  this.modal?.setConfirmPending(true);
181
- const res = await this.fetchWithTimeout(`${this.getApiBaseUrl()}/api/merchant/checkout/sessions/${this.sessionId}/confirm-payment`, {
182
- method: "POST",
183
- headers: {
184
- "Content-Type": "application/json",
185
- "x-public-key": this.cfg.publicKey,
186
- },
187
- body: "{}",
188
- });
189
- if (!res.ok) {
190
+ try {
191
+ const res = await this.fetchWithTimeout(`${this.getApiBaseUrl()}/api/merchant/checkout/sessions/${this.sessionId}/confirm-payment`, {
192
+ method: "POST",
193
+ headers: {
194
+ "Content-Type": "application/json",
195
+ "x-public-key": this.cfg.publicKey,
196
+ },
197
+ body: "{}",
198
+ });
199
+ if (!res.ok) {
200
+ console.log("[T9N] confirmPayment: non-ok response", { status: res.status, sessionId: this.sessionId });
201
+ this.modal?.setConfirmPending(false);
202
+ this.modal?.showResult("failed", "Verification failed. Please try again.");
203
+ this.lastResultFailed = true;
204
+ this.modal?.setConfirmLabel("Retry check");
205
+ this.emitStatus("failed");
206
+ this.emitFailOnce({ sessionId: this.sessionId || undefined, error: "confirm request failed" });
207
+ return;
208
+ }
209
+ let payload = {};
210
+ try {
211
+ payload = (await res.json());
212
+ console.log("[T9N] confirmPayment: response payload", { payload, sessionId: this.sessionId });
213
+ }
214
+ catch (_) {
215
+ console.log("[T9N] confirmPayment: invalid json response", { sessionId: this.sessionId });
216
+ this.modal?.setConfirmPending(false);
217
+ this.modal?.showResult("failed", "Verification failed. Please try again.");
218
+ this.lastResultFailed = true;
219
+ this.modal?.setConfirmLabel("Retry check");
220
+ this.emitStatus("failed");
221
+ this.emitFailOnce({ sessionId: this.sessionId || undefined, error: "invalid confirm response" });
222
+ return;
223
+ }
190
224
  this.modal?.setConfirmPending(false);
191
- this.modal?.showResult("failed", "No payment detected yet. Please try again.");
192
- this.lastResultFailed = true;
193
- this.modal?.setConfirmLabel("Retry check");
194
- this.emitStatus("pending_confirmation");
195
- this.emitFailOnce({ sessionId: this.sessionId || undefined, error: "confirm request failed" });
196
- throw new Error("T9N: failed to confirm payment");
197
- }
198
- const payload = (await res.json());
199
- this.modal?.setConfirmPending(false);
200
- const normalized = this.normalizeStatus(payload.status);
201
- this.emitStatus(normalized);
202
- if (payload.status === "pending" ||
203
- payload.status === "processing" ||
204
- normalized === "pending_confirmation" ||
205
- normalized === "awaiting_payment") {
225
+ const normalized = this.normalizeStatus(payload.status || "");
226
+ console.log("[T9N] confirmPayment: normalized status", { status: payload.status, normalized, sessionId: this.sessionId });
227
+ this.emitStatus(normalized);
228
+ if (normalized === "settled") {
229
+ console.log("[T9N] confirmPayment: showing success", { sessionId: this.sessionId });
230
+ this.modal?.setConfirmLabel("I have made the payment");
231
+ this.modal?.showResult("success", "Payment received successfully.");
232
+ this.lastResultFailed = false;
233
+ this.emitSuccessOnce({ sessionId: this.sessionId, status: payload.status || "settled" });
234
+ return;
235
+ }
236
+ if (normalized === "expired") {
237
+ console.log("[T9N] confirmPayment: showing expired", { sessionId: this.sessionId });
238
+ this.modal?.showResult("failed", "This session has expired.");
239
+ this.lastResultFailed = false;
240
+ this.emitFailOnce({ sessionId: this.sessionId, error: "checkout expired" });
241
+ this.scheduleAutoClose();
242
+ return;
243
+ }
244
+ if (normalized === "failed") {
245
+ console.log("[T9N] confirmPayment: showing failed", { sessionId: this.sessionId });
246
+ this.modal?.showResult("failed", "Payment failed. Please try again.");
247
+ this.lastResultFailed = true;
248
+ this.modal?.setConfirmLabel("Retry check");
249
+ this.emitFailOnce({ sessionId: this.sessionId || undefined, error: "checkout failed" });
250
+ return;
251
+ }
252
+ if (normalized === "closed") {
253
+ console.log("[T9N] confirmPayment: showing closed", { sessionId: this.sessionId });
254
+ this.modal?.showResult("failed", "Checkout was closed. Please try again.");
255
+ this.lastResultFailed = true;
256
+ this.modal?.setConfirmLabel("Retry check");
257
+ this.emitFailOnce({ sessionId: this.sessionId || undefined, error: "checkout closed" });
258
+ return;
259
+ }
260
+ console.log("[T9N] confirmPayment: showing no-funds retry", { sessionId: this.sessionId });
206
261
  this.modal?.showResult("failed", "No payment detected yet. Please try again.");
207
262
  this.lastResultFailed = true;
208
263
  this.modal?.setConfirmLabel("Retry check");
209
264
  this.emitFailOnce({ sessionId: this.sessionId || undefined, error: "payment not detected" });
210
- return;
211
265
  }
212
- this.modal?.setConfirmLabel("I have made the payment");
213
- if (payload.status === "settled") {
214
- this.modal?.showResult("success", "Payment received successfully.");
215
- this.lastResultFailed = false;
216
- this.emitSuccessOnce({ sessionId: this.sessionId, status: payload.status });
217
- }
218
- if (payload.status === "expired") {
219
- this.modal?.showResult("failed", "This session has expired.");
266
+ catch (err) {
267
+ console.log("[T9N] confirmPayment: error", { error: err?.message || err, sessionId: this.sessionId });
268
+ this.modal?.setConfirmPending(false);
269
+ this.modal?.showResult("failed", "Verification failed. Please try again.");
220
270
  this.lastResultFailed = true;
221
- this.emitFailOnce({ sessionId: this.sessionId, error: "checkout expired" });
271
+ this.modal?.setConfirmLabel("Retry check");
272
+ this.emitStatus("failed");
273
+ this.emitFailOnce({
274
+ sessionId: this.sessionId || undefined,
275
+ error: err?.message || "confirm request failed",
276
+ });
222
277
  }
223
278
  }
224
279
  startTimer() {
@@ -228,8 +283,14 @@ export class T9nCheckout {
228
283
  const ms = this.expiresAt.getTime() - Date.now();
229
284
  if (ms <= 0) {
230
285
  this.modal?.setTimer("00:00");
231
- this.modal?.setStatus("Session expired");
232
- this.close();
286
+ if (this.intervalId)
287
+ window.clearInterval(this.intervalId);
288
+ if (this.statusPollId)
289
+ window.clearInterval(this.statusPollId);
290
+ this.modal?.showResult("failed", "This session has expired.");
291
+ this.lastResultFailed = false;
292
+ this.emitFailOnce({ sessionId: this.sessionId || undefined, error: "checkout expired" });
293
+ this.scheduleAutoClose();
233
294
  return;
234
295
  }
235
296
  const minutes = Math.floor(ms / 60000)
@@ -255,8 +316,10 @@ export class T9nCheckout {
255
316
  const payload = (await res.json());
256
317
  if (payload.status) {
257
318
  const normalized = this.normalizeStatus(payload.status);
319
+ console.log("[T9N] poll: status", { status: payload.status, normalized, sessionId: this.sessionId });
258
320
  this.emitStatus(normalized);
259
321
  if (normalized === "settled") {
322
+ console.log("[T9N] poll: showing success", { sessionId: this.sessionId });
260
323
  this.modal?.showResult("success", "Payment received successfully.");
261
324
  this.lastResultFailed = false;
262
325
  if (this.intervalId)
@@ -267,6 +330,7 @@ export class T9nCheckout {
267
330
  }
268
331
  if (normalized === "failed") {
269
332
  if (this.hasAttemptedConfirm) {
333
+ console.log("[T9N] poll: showing failed", { sessionId: this.sessionId });
270
334
  this.modal?.showResult("failed", "No payment detected yet. Please try again.");
271
335
  this.lastResultFailed = true;
272
336
  this.emitFailOnce({ sessionId: this.sessionId, error: "checkout failed" });
@@ -274,6 +338,7 @@ export class T9nCheckout {
274
338
  }
275
339
  if (normalized === "pending_confirmation" || normalized === "awaiting_payment") {
276
340
  if (this.hasAttemptedConfirm) {
341
+ console.log("[T9N] poll: showing pending no-funds", { sessionId: this.sessionId });
277
342
  this.modal?.showResult("failed", "No payment detected yet. Please try again.");
278
343
  this.lastResultFailed = true;
279
344
  this.emitFailOnce({ sessionId: this.sessionId || undefined, error: "payment not detected" });
@@ -281,13 +346,15 @@ export class T9nCheckout {
281
346
  }
282
347
  }
283
348
  if (payload.status === "expired") {
349
+ console.log("[T9N] poll: showing expired", { sessionId: this.sessionId });
284
350
  this.modal?.showResult("failed", "This session has expired.");
285
- this.lastResultFailed = true;
351
+ this.lastResultFailed = false;
286
352
  if (this.intervalId)
287
353
  window.clearInterval(this.intervalId);
288
354
  if (this.statusPollId)
289
355
  window.clearInterval(this.statusPollId);
290
356
  this.emitFailOnce({ sessionId: this.sessionId, error: "checkout expired" });
357
+ this.scheduleAutoClose();
291
358
  }
292
359
  }
293
360
  catch (_) {
@@ -399,6 +466,16 @@ export class T9nCheckout {
399
466
  break;
400
467
  }
401
468
  }
469
+ scheduleAutoClose(delayMs = 1200) {
470
+ this.skipCloseMark = true;
471
+ if (this.closeTimeoutId) {
472
+ window.clearTimeout(this.closeTimeoutId);
473
+ }
474
+ this.closeTimeoutId = window.setTimeout(() => {
475
+ if (this.isOpen)
476
+ this.close();
477
+ }, delayMs);
478
+ }
402
479
  normalizeStatus(value) {
403
480
  switch (value) {
404
481
  case "created":
@@ -406,6 +483,10 @@ export class T9nCheckout {
406
483
  return "awaiting_payment";
407
484
  case "awaiting_payment":
408
485
  case "pending_confirmation":
486
+ return value;
487
+ case "pending":
488
+ case "processing":
489
+ return "pending_confirmation";
409
490
  case "closed":
410
491
  case "expired":
411
492
  case "settled":
@@ -452,12 +533,14 @@ export class T9nCheckout {
452
533
  if (this.successNotified)
453
534
  return;
454
535
  this.successNotified = true;
536
+ console.log("[T9N] onSuccess", payload);
455
537
  this.cfg.hooks?.onSuccess?.(payload);
456
538
  }
457
539
  emitFailOnce(payload) {
458
540
  if (this.successNotified || this.failureNotified)
459
541
  return;
460
542
  this.failureNotified = true;
543
+ console.log("[T9N] onFail", payload);
461
544
  this.cfg.hooks?.onFail?.(payload);
462
545
  }
463
546
  getApiBaseUrl() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tineon/t9n",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",