@tineon/t9n 0.1.6 → 0.1.8

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 = "";
@@ -178,46 +186,83 @@ export class T9nCheckout {
178
186
  async confirmPayment() {
179
187
  this.hasAttemptedConfirm = true;
180
188
  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) {
189
+ try {
190
+ const res = await this.fetchWithTimeout(`${this.getApiBaseUrl()}/api/merchant/checkout/sessions/${this.sessionId}/confirm-payment`, {
191
+ method: "POST",
192
+ headers: {
193
+ "Content-Type": "application/json",
194
+ "x-public-key": this.cfg.publicKey,
195
+ },
196
+ body: "{}",
197
+ });
198
+ if (!res.ok) {
199
+ this.modal?.setConfirmPending(false);
200
+ this.modal?.showResult("failed", "Verification failed. Please try again.");
201
+ this.lastResultFailed = true;
202
+ this.modal?.setConfirmLabel("Retry check");
203
+ this.emitStatus("failed");
204
+ this.emitFailOnce({ sessionId: this.sessionId || undefined, error: "confirm request failed" });
205
+ return;
206
+ }
207
+ let payload = {};
208
+ try {
209
+ payload = (await res.json());
210
+ }
211
+ catch (_) {
212
+ this.modal?.setConfirmPending(false);
213
+ this.modal?.showResult("failed", "Verification failed. Please try again.");
214
+ this.lastResultFailed = true;
215
+ this.modal?.setConfirmLabel("Retry check");
216
+ this.emitStatus("failed");
217
+ this.emitFailOnce({ sessionId: this.sessionId || undefined, error: "invalid confirm response" });
218
+ return;
219
+ }
190
220
  this.modal?.setConfirmPending(false);
221
+ const normalized = this.normalizeStatus(payload.status || "");
222
+ this.emitStatus(normalized);
223
+ if (normalized === "settled") {
224
+ this.modal?.setConfirmLabel("I have made the payment");
225
+ this.modal?.showResult("success", "Payment received successfully.");
226
+ this.lastResultFailed = false;
227
+ this.emitSuccessOnce({ sessionId: this.sessionId, status: payload.status || "settled" });
228
+ return;
229
+ }
230
+ if (normalized === "expired") {
231
+ this.modal?.showResult("failed", "This session has expired.");
232
+ this.lastResultFailed = false;
233
+ this.emitFailOnce({ sessionId: this.sessionId, error: "checkout expired" });
234
+ this.scheduleAutoClose();
235
+ return;
236
+ }
237
+ if (normalized === "failed") {
238
+ this.modal?.showResult("failed", "Payment failed. Please try again.");
239
+ this.lastResultFailed = true;
240
+ this.modal?.setConfirmLabel("Retry check");
241
+ this.emitFailOnce({ sessionId: this.sessionId || undefined, error: "checkout failed" });
242
+ return;
243
+ }
244
+ if (normalized === "closed") {
245
+ this.modal?.showResult("failed", "Checkout was closed. Please try again.");
246
+ this.lastResultFailed = true;
247
+ this.modal?.setConfirmLabel("Retry check");
248
+ this.emitFailOnce({ sessionId: this.sessionId || undefined, error: "checkout closed" });
249
+ return;
250
+ }
191
251
  this.modal?.showResult("failed", "No payment detected yet. Please try again.");
192
252
  this.lastResultFailed = true;
193
253
  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");
254
+ this.emitFailOnce({ sessionId: this.sessionId || undefined, error: "payment not detected" });
197
255
  }
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") {
206
- this.modal?.showResult("failed", "No payment detected yet. Please try again.");
256
+ catch (err) {
257
+ this.modal?.setConfirmPending(false);
258
+ this.modal?.showResult("failed", "Verification failed. Please try again.");
207
259
  this.lastResultFailed = true;
208
260
  this.modal?.setConfirmLabel("Retry check");
209
- return;
210
- }
211
- this.modal?.setConfirmLabel("I have made the payment");
212
- if (payload.status === "settled") {
213
- this.modal?.showResult("success", "Payment received successfully.");
214
- this.lastResultFailed = false;
215
- this.emitSuccessOnce({ sessionId: this.sessionId, status: payload.status });
216
- }
217
- if (payload.status === "expired") {
218
- this.modal?.showResult("failed", "This session has expired.");
219
- this.lastResultFailed = true;
220
- this.emitFailOnce({ sessionId: this.sessionId, error: "checkout expired" });
261
+ this.emitStatus("failed");
262
+ this.emitFailOnce({
263
+ sessionId: this.sessionId || undefined,
264
+ error: err?.message || "confirm request failed",
265
+ });
221
266
  }
222
267
  }
223
268
  startTimer() {
@@ -227,8 +272,14 @@ export class T9nCheckout {
227
272
  const ms = this.expiresAt.getTime() - Date.now();
228
273
  if (ms <= 0) {
229
274
  this.modal?.setTimer("00:00");
230
- this.modal?.setStatus("Session expired");
231
- this.close();
275
+ if (this.intervalId)
276
+ window.clearInterval(this.intervalId);
277
+ if (this.statusPollId)
278
+ window.clearInterval(this.statusPollId);
279
+ this.modal?.showResult("failed", "This session has expired.");
280
+ this.lastResultFailed = false;
281
+ this.emitFailOnce({ sessionId: this.sessionId || undefined, error: "checkout expired" });
282
+ this.scheduleAutoClose();
232
283
  return;
233
284
  }
234
285
  const minutes = Math.floor(ms / 60000)
@@ -275,17 +326,19 @@ export class T9nCheckout {
275
326
  if (this.hasAttemptedConfirm) {
276
327
  this.modal?.showResult("failed", "No payment detected yet. Please try again.");
277
328
  this.lastResultFailed = true;
329
+ this.emitFailOnce({ sessionId: this.sessionId || undefined, error: "payment not detected" });
278
330
  }
279
331
  }
280
332
  }
281
333
  if (payload.status === "expired") {
282
334
  this.modal?.showResult("failed", "This session has expired.");
283
- this.lastResultFailed = true;
335
+ this.lastResultFailed = false;
284
336
  if (this.intervalId)
285
337
  window.clearInterval(this.intervalId);
286
338
  if (this.statusPollId)
287
339
  window.clearInterval(this.statusPollId);
288
340
  this.emitFailOnce({ sessionId: this.sessionId, error: "checkout expired" });
341
+ this.scheduleAutoClose();
289
342
  }
290
343
  }
291
344
  catch (_) {
@@ -397,6 +450,16 @@ export class T9nCheckout {
397
450
  break;
398
451
  }
399
452
  }
453
+ scheduleAutoClose(delayMs = 1200) {
454
+ this.skipCloseMark = true;
455
+ if (this.closeTimeoutId) {
456
+ window.clearTimeout(this.closeTimeoutId);
457
+ }
458
+ this.closeTimeoutId = window.setTimeout(() => {
459
+ if (this.isOpen)
460
+ this.close();
461
+ }, delayMs);
462
+ }
400
463
  normalizeStatus(value) {
401
464
  switch (value) {
402
465
  case "created":
@@ -404,6 +467,10 @@ export class T9nCheckout {
404
467
  return "awaiting_payment";
405
468
  case "awaiting_payment":
406
469
  case "pending_confirmation":
470
+ return value;
471
+ case "pending":
472
+ case "processing":
473
+ return "pending_confirmation";
407
474
  case "closed":
408
475
  case "expired":
409
476
  case "settled":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tineon/t9n",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",