@tineon/t9n 0.1.7 → 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,47 +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);
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") {
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
+ }
206
251
  this.modal?.showResult("failed", "No payment detected yet. Please try again.");
207
252
  this.lastResultFailed = true;
208
253
  this.modal?.setConfirmLabel("Retry check");
209
254
  this.emitFailOnce({ sessionId: this.sessionId || undefined, error: "payment not detected" });
210
- return;
211
255
  }
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.");
256
+ catch (err) {
257
+ this.modal?.setConfirmPending(false);
258
+ this.modal?.showResult("failed", "Verification failed. Please try again.");
220
259
  this.lastResultFailed = true;
221
- this.emitFailOnce({ sessionId: this.sessionId, error: "checkout expired" });
260
+ this.modal?.setConfirmLabel("Retry check");
261
+ this.emitStatus("failed");
262
+ this.emitFailOnce({
263
+ sessionId: this.sessionId || undefined,
264
+ error: err?.message || "confirm request failed",
265
+ });
222
266
  }
223
267
  }
224
268
  startTimer() {
@@ -228,8 +272,14 @@ export class T9nCheckout {
228
272
  const ms = this.expiresAt.getTime() - Date.now();
229
273
  if (ms <= 0) {
230
274
  this.modal?.setTimer("00:00");
231
- this.modal?.setStatus("Session expired");
232
- 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();
233
283
  return;
234
284
  }
235
285
  const minutes = Math.floor(ms / 60000)
@@ -282,12 +332,13 @@ export class T9nCheckout {
282
332
  }
283
333
  if (payload.status === "expired") {
284
334
  this.modal?.showResult("failed", "This session has expired.");
285
- this.lastResultFailed = true;
335
+ this.lastResultFailed = false;
286
336
  if (this.intervalId)
287
337
  window.clearInterval(this.intervalId);
288
338
  if (this.statusPollId)
289
339
  window.clearInterval(this.statusPollId);
290
340
  this.emitFailOnce({ sessionId: this.sessionId, error: "checkout expired" });
341
+ this.scheduleAutoClose();
291
342
  }
292
343
  }
293
344
  catch (_) {
@@ -399,6 +450,16 @@ export class T9nCheckout {
399
450
  break;
400
451
  }
401
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
+ }
402
463
  normalizeStatus(value) {
403
464
  switch (value) {
404
465
  case "created":
@@ -406,6 +467,10 @@ export class T9nCheckout {
406
467
  return "awaiting_payment";
407
468
  case "awaiting_payment":
408
469
  case "pending_confirmation":
470
+ return value;
471
+ case "pending":
472
+ case "processing":
473
+ return "pending_confirmation";
409
474
  case "closed":
410
475
  case "expired":
411
476
  case "settled":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tineon/t9n",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",