@alipay/ams-checkout 0.0.1783663570-dev.6 → 0.0.1783663570-dev.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.
@@ -87,6 +87,17 @@ var recoverSdkQueryBusinessResponse = function recoverSdkQueryBusinessResponse(e
87
87
  };
88
88
  var MOUNT_TIMEOUT = 16000;
89
89
  var logger = new Logger(LogConfig, true);
90
+ var ELEMENT_DESTROYED_MESSAGE = 'Element already destroyed';
91
+ var getDestroyedError = function getDestroyedError() {
92
+ return {
93
+ code: ERRORMESSAGE.UI_STATE_ERROR.code,
94
+ message: ELEMENT_DESTROYED_MESSAGE
95
+ };
96
+ };
97
+ var isDestroyedError = function isDestroyedError(error) {
98
+ var maybeError = error;
99
+ return maybeError.code === ERRORMESSAGE.UI_STATE_ERROR.code && maybeError.message === ELEMENT_DESTROYED_MESSAGE;
100
+ };
90
101
  /**
91
102
  * HostSDKBridge 扩展 SDKBridge,暴露 setTarget 方法供 HOST 模式使用
92
103
  */
@@ -178,12 +189,15 @@ export var ModernElementController = /*#__PURE__*/function () {
178
189
  _defineProperty(this, "notRedirectAfterComplete", false);
179
190
  _defineProperty(this, "_sdkInitTime", Date.now());
180
191
  _defineProperty(this, "_mountStartTime", 0);
192
+ _defineProperty(this, "destroyCallbacks", []);
181
193
  /** 商户传入的金额覆盖(Apple Pay 弹窗展示用,实际扣款由服务端控制) */
182
194
  _defineProperty(this, "_merchantAmount", null);
183
195
  /** 商户传入的币种覆盖(Apple Pay 弹窗展示用) */
184
196
  _defineProperty(this, "_merchantCurrency", null);
185
197
  /** 当前选中的支付方式类型(如 'APPLEPAY', 'CARD'),通过 paymentMethodChanged 事件更新 */
186
198
  _defineProperty(this, "currentPaymentMethodType", '');
199
+ /** 提交进行中标志:submitPayment 执行期间为 true,用于拒绝并发重入,避免旧闭包串扰/重复拉起钱包 */
200
+ _defineProperty(this, "isSubmitting", false);
187
201
  this.options = options;
188
202
  this.onStatusChange = onStatusChange;
189
203
  if (options.sessionData) {
@@ -278,6 +292,12 @@ export var ModernElementController = /*#__PURE__*/function () {
278
292
  _yield$Promise$all = _context.sent;
279
293
  _yield$Promise$all2 = _slicedToArray(_yield$Promise$all, 1);
280
294
  setupConfig = _yield$Promise$all2[0];
295
+ if (!(this.status === 'destroyed')) {
296
+ _context.next = 29;
297
+ break;
298
+ }
299
+ throw getDestroyedError();
300
+ case 29:
281
301
  dataStructError = this.validateRenderData(setupConfig.renderData, setupConfig.renderDataError);
282
302
  if (dataStructError) {
283
303
  setupConfig.renderDataError = dataStructError;
@@ -292,32 +312,40 @@ export var ModernElementController = /*#__PURE__*/function () {
292
312
  this.sendHandshake(setupConfig);
293
313
 
294
314
  // 5. 等待 HANDSHAKE_ACK
295
- _context.next = 33;
315
+ _context.next = 35;
296
316
  return this.waitForHandshakeAck();
297
- case 33:
317
+ case 35:
298
318
  this.setStatus('ready');
299
319
  this.flushCachedMessages();
300
320
  (_this$options$loading3 = this.options.loading) === null || _this$options$loading3 === void 0 || (_this$options$loading4 = _this$options$loading3.onEndLoading) === null || _this$options$loading4 === void 0 || _this$options$loading4.call(_this$options$loading3);
301
321
  if (!dataStructError) {
302
- _context.next = 38;
322
+ _context.next = 40;
303
323
  break;
304
324
  }
305
325
  return _context.abrupt("return", {
306
326
  error: dataStructError
307
327
  });
308
- case 38:
328
+ case 40:
309
329
  trackMount('mount_success', {
310
330
  m1: this._mountStartTime ? Date.now() - this._mountStartTime : 0
311
331
  });
312
332
  return _context.abrupt("return", {});
313
- case 42:
314
- _context.prev = 42;
333
+ case 44:
334
+ _context.prev = 44;
315
335
  _context.t0 = _context["catch"](8);
336
+ (_this$options$loading5 = this.options.loading) === null || _this$options$loading5 === void 0 || (_this$options$loading6 = _this$options$loading5.onEndLoading) === null || _this$options$loading6 === void 0 || _this$options$loading6.call(_this$options$loading5);
337
+ if (!isDestroyedError(_context.t0)) {
338
+ _context.next = 49;
339
+ break;
340
+ }
341
+ return _context.abrupt("return", {
342
+ error: _context.t0
343
+ });
344
+ case 49:
316
345
  trackError('mount_error', {
317
346
  d1: (_context.t0 === null || _context.t0 === void 0 ? void 0 : _context.t0.code) || 'UNKNOWN',
318
347
  d2: (_context.t0 === null || _context.t0 === void 0 ? void 0 : _context.t0.message) || ''
319
348
  });
320
- (_this$options$loading5 = this.options.loading) === null || _this$options$loading5 === void 0 || (_this$options$loading6 = _this$options$loading5.onEndLoading) === null || _this$options$loading6 === void 0 || _this$options$loading6.call(_this$options$loading5);
321
349
  this.cleanup();
322
350
  errorResult = {
323
351
  code: _context.t0.code || 'UNKNOWN',
@@ -327,11 +355,11 @@ export var ModernElementController = /*#__PURE__*/function () {
327
355
  return _context.abrupt("return", {
328
356
  error: errorResult
329
357
  });
330
- case 50:
358
+ case 54:
331
359
  case "end":
332
360
  return _context.stop();
333
361
  }
334
- }, _callee, this, [[8, 42]]);
362
+ }, _callee, this, [[8, 44]]);
335
363
  }));
336
364
  function mount(_x, _x2) {
337
365
  return _mount.apply(this, arguments);
@@ -356,7 +384,7 @@ export var ModernElementController = /*#__PURE__*/function () {
356
384
  _this = this,
357
385
  _parseData$paymentMet,
358
386
  _sdkQueryResult$data;
359
- var sessionData, parseData, isConnect, paymentSessionConfig, genSdkQueryPromise, genOneAccountQueryPromise, promises, _ref2, _ref3, sdkQueryResult, oneAccountQueryResult, _sdkQueryResult$error, _sdkQueryResult$error2, _oneAccountQueryResul, _oneAccountQueryResul2, preferredLocale, locale, renderDataError;
387
+ var sessionData, parseData, isConnect, paymentSessionConfig, genSdkQueryPromise, genOneAccountQueryPromise, promises, cancelDestroyCallback, destroyPromise, queryPromise, _yield$Promise$race$f, _yield$Promise$race$f2, sdkQueryResult, oneAccountQueryResult, _sdkQueryResult$error, _sdkQueryResult$error2, _oneAccountQueryResul, _oneAccountQueryResul2, preferredLocale, locale, renderDataError;
360
388
  return _regeneratorRuntime().wrap(function _callee2$(_context2) {
361
389
  while (1) switch (_context2.prev = _context2.next) {
362
390
  case 0:
@@ -372,7 +400,7 @@ export var ModernElementController = /*#__PURE__*/function () {
372
400
  request.setGlobalOptions({
373
401
  hostSign: parseHostSign(sessionData),
374
402
  env: this.options.environment,
375
- sdkVersion: "0.0.1783663570-dev.6" || '',
403
+ sdkVersion: "0.0.1783663570-dev.8" || '',
376
404
  tracker: sdkTracker
377
405
  });
378
406
  isConnect = !!(parseData !== null && parseData !== void 0 && (_parseData$connectFac = parseData.connectFactor) !== null && _parseData$connectFac !== void 0 && _parseData$connectFac.enableConnect);
@@ -405,13 +433,20 @@ export var ModernElementController = /*#__PURE__*/function () {
405
433
  }));
406
434
  };
407
435
  promises = isConnect ? [genSdkQueryPromise(), genOneAccountQueryPromise()] : [genSdkQueryPromise(), undefined];
408
- _context2.next = 13;
409
- return Promise.all(promises);
410
- case 13:
411
- _ref2 = _context2.sent;
412
- _ref3 = _slicedToArray(_ref2, 2);
413
- sdkQueryResult = _ref3[0];
414
- oneAccountQueryResult = _ref3[1];
436
+ cancelDestroyCallback = function cancelDestroyCallback() {};
437
+ destroyPromise = new Promise(function (_, reject) {
438
+ cancelDestroyCallback = _this.onDestroy(function () {
439
+ return reject(getDestroyedError());
440
+ });
441
+ });
442
+ queryPromise = Promise.all(promises);
443
+ _context2.next = 16;
444
+ return Promise.race([queryPromise, destroyPromise]).finally(cancelDestroyCallback);
445
+ case 16:
446
+ _yield$Promise$race$f = _context2.sent;
447
+ _yield$Promise$race$f2 = _slicedToArray(_yield$Promise$race$f, 2);
448
+ sdkQueryResult = _yield$Promise$race$f2[0];
449
+ oneAccountQueryResult = _yield$Promise$race$f2[1];
415
450
  if (sdkQueryResult.error) {
416
451
  trackHandshake('sdk_query_failed', {
417
452
  d1: ((_sdkQueryResult$error = sdkQueryResult.error) === null || _sdkQueryResult$error === void 0 ? void 0 : _sdkQueryResult$error.code) || '',
@@ -461,7 +496,7 @@ export var ModernElementController = /*#__PURE__*/function () {
461
496
  // 新sdk字段
462
497
  locale: locale,
463
498
  // 新sdk字段
464
- sdkVersion: "0.0.1783663570-dev.6" || '',
499
+ sdkVersion: "0.0.1783663570-dev.8" || '',
465
500
  // 新sdk字段
466
501
  merchantId: (sdkQueryResult === null || sdkQueryResult === void 0 || (_sdkQueryResult$data = sdkQueryResult.data) === null || _sdkQueryResult$data === void 0 ? void 0 : _sdkQueryResult$data.merchantId) || '',
467
502
  // 新sdk字段
@@ -471,7 +506,7 @@ export var ModernElementController = /*#__PURE__*/function () {
471
506
  env: this.options.environment === 'sandbox' ? 'sandbox' : 'prod'
472
507
  }, getDevReqConfig())
473
508
  });
474
- case 23:
509
+ case 26:
475
510
  case "end":
476
511
  return _context2.stop();
477
512
  }
@@ -543,10 +578,10 @@ export var ModernElementController = /*#__PURE__*/function () {
543
578
  // 金额存在性校验
544
579
  var hasAmountViews = !!orderAmountView && !!paymentAmountView;
545
580
  if (!success) {
546
- var _ref4 = renderData,
547
- traceId = _ref4.traceId,
548
- errorCode = _ref4.errorCode,
549
- errorMessage = _ref4.errorMessage;
581
+ var _ref2 = renderData,
582
+ traceId = _ref2.traceId,
583
+ errorCode = _ref2.errorCode,
584
+ errorMessage = _ref2.errorMessage;
550
585
  return this.getLegacySdkQueryError({
551
586
  errorCode: errorCode,
552
587
  errorMessage: errorMessage,
@@ -580,7 +615,7 @@ export var ModernElementController = /*#__PURE__*/function () {
580
615
  });
581
616
  this.bridge.on(MessageType.REDIRECT, function (payload, _ports, meta) {
582
617
  var _this2$iframe;
583
- if ((meta === null || meta === void 0 ? void 0 : meta.source) !== ((_this2$iframe = _this2.iframe) === null || _this2$iframe === void 0 ? void 0 : _this2$iframe.contentWindow)) {
618
+ if (meta && meta.source !== ((_this2$iframe = _this2.iframe) === null || _this2$iframe === void 0 ? void 0 : _this2$iframe.contentWindow)) {
584
619
  return;
585
620
  }
586
621
  var url = payload.url,
@@ -635,13 +670,21 @@ export var ModernElementController = /*#__PURE__*/function () {
635
670
  fallbackJump();
636
671
  }
637
672
  });
638
- this.bridge.on(MessageType.OPEN_MODAL, function (payload) {
673
+ this.bridge.on(MessageType.OPEN_MODAL, function (payload, _ports, meta) {
674
+ var _this2$iframe2;
675
+ if (meta && meta.source !== ((_this2$iframe2 = _this2.iframe) === null || _this2$iframe2 === void 0 ? void 0 : _this2$iframe2.contentWindow)) {
676
+ return;
677
+ }
639
678
  _this2.handleOpenModal(payload);
640
- _this2.bridge.on(MessageType.CLOSE_MODAL, function () {
679
+ _this2.bridge.once(MessageType.CLOSE_MODAL, function () {
641
680
  _this2.handleCloseModal();
642
681
  });
643
682
  });
644
- this.bridge.on(MessageType.DESTROY_ELEMENT, function () {
683
+ this.bridge.on(MessageType.DESTROY_ELEMENT, function (_payload, _ports, meta) {
684
+ var _this2$iframe3;
685
+ if (meta && meta.source !== ((_this2$iframe3 = _this2.iframe) === null || _this2$iframe3 === void 0 ? void 0 : _this2$iframe3.contentWindow)) {
686
+ return;
687
+ }
645
688
  _this2.destroy();
646
689
  });
647
690
 
@@ -771,12 +814,12 @@ export var ModernElementController = /*#__PURE__*/function () {
771
814
  return validateMerchant;
772
815
  }(),
773
816
  onAuthorized: function () {
774
- var _onAuthorized = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee4(_ref5) {
817
+ var _onAuthorized = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee4(_ref3) {
775
818
  var token, billingContact, shippingContact, startTime, _yield$executeWithTim, rawResult, timedOut, shouldContinue;
776
819
  return _regeneratorRuntime().wrap(function _callee4$(_context4) {
777
820
  while (1) switch (_context4.prev = _context4.next) {
778
821
  case 0:
779
- token = _ref5.token, billingContact = _ref5.billingContact, shippingContact = _ref5.shippingContact;
822
+ token = _ref3.token, billingContact = _ref3.billingContact, shippingContact = _ref3.shippingContact;
780
823
  if (!onBeforeConfirm) {
781
824
  _context4.next = 18;
782
825
  break;
@@ -1070,9 +1113,9 @@ export var ModernElementController = /*#__PURE__*/function () {
1070
1113
  var startTime = Date.now();
1071
1114
  executeWithTimeout(function () {
1072
1115
  return onBeforeConfirm();
1073
- }).then(function (_ref6) {
1074
- var rawResult = _ref6.result,
1075
- timedOut = _ref6.timedOut;
1116
+ }).then(function (_ref4) {
1117
+ var rawResult = _ref4.result,
1118
+ timedOut = _ref4.timedOut;
1076
1119
  // 双重校验:timedOut(Promise race)+ startTime(实际时间差),与老 ElementController 对齐
1077
1120
  var shouldContinue = !timedOut && !!rawResult && Date.now() - startTime < BEFORE_CONFIRM_TIMEOUT_MS;
1078
1121
  if (timedOut || Date.now() - startTime >= BEFORE_CONFIRM_TIMEOUT_MS) {
@@ -1112,7 +1155,9 @@ export var ModernElementController = /*#__PURE__*/function () {
1112
1155
  value: function waitForReadyHandshake() {
1113
1156
  var _this6 = this;
1114
1157
  return new Promise(function (resolve, reject) {
1158
+ var cancelDestroyCallback = function cancelDestroyCallback() {};
1115
1159
  var timer = setTimeout(function () {
1160
+ cancelDestroyCallback();
1116
1161
  trackError('ready_handshake_timeout', {
1117
1162
  d1: String(MOUNT_TIMEOUT)
1118
1163
  });
@@ -1121,9 +1166,14 @@ export var ModernElementController = /*#__PURE__*/function () {
1121
1166
  message: 'An error occurred; the transaction cannot be initiated.'
1122
1167
  });
1123
1168
  }, MOUNT_TIMEOUT);
1169
+ cancelDestroyCallback = _this6.onDestroy(function () {
1170
+ clearTimeout(timer);
1171
+ reject(getDestroyedError());
1172
+ });
1124
1173
  _this6.bridge.once(MessageType.READY_HANDSHAKE, function () {
1125
1174
  var _this6$iframe;
1126
1175
  clearTimeout(timer);
1176
+ cancelDestroyCallback();
1127
1177
  if (!((_this6$iframe = _this6.iframe) !== null && _this6$iframe !== void 0 && _this6$iframe.contentWindow)) {
1128
1178
  trackError('iframe_contentwindow_unavailable', {
1129
1179
  d1: 'after_ready_handshake'
@@ -1163,7 +1213,9 @@ export var ModernElementController = /*#__PURE__*/function () {
1163
1213
  value: function waitForHandshakeAck() {
1164
1214
  var _this7 = this;
1165
1215
  return new Promise(function (resolve, reject) {
1216
+ var cancelDestroyCallback = function cancelDestroyCallback() {};
1166
1217
  var timer = setTimeout(function () {
1218
+ cancelDestroyCallback();
1167
1219
  trackError('handshake_ack_timeout', {
1168
1220
  d1: String(MOUNT_TIMEOUT)
1169
1221
  });
@@ -1172,8 +1224,13 @@ export var ModernElementController = /*#__PURE__*/function () {
1172
1224
  message: 'An error occurred; the transaction cannot be initiated.'
1173
1225
  });
1174
1226
  }, MOUNT_TIMEOUT);
1227
+ cancelDestroyCallback = _this7.onDestroy(function () {
1228
+ clearTimeout(timer);
1229
+ reject(getDestroyedError());
1230
+ });
1175
1231
  _this7.bridge.once(MessageType.HANDSHAKE_ACK, function () {
1176
1232
  clearTimeout(timer);
1233
+ cancelDestroyCallback();
1177
1234
  trackHandshake('handshake_ack_received', {
1178
1235
  m1: _this7._mountStartTime ? Date.now() - _this7._mountStartTime : 0
1179
1236
  });
@@ -1185,10 +1242,8 @@ export var ModernElementController = /*#__PURE__*/function () {
1185
1242
  key: "submitPayment",
1186
1243
  value: function () {
1187
1244
  var _submitPayment = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee6(params) {
1188
- var _this$options$loading7,
1189
- _this$options$loading8,
1190
- _this8 = this;
1191
- var isApplePay, _ref7, onBeforeConfirm, serializableParams, startTime, _yield$executeWithTim2, shouldContinue, timedOut, hasBeforeConfirmForApplePay, hasAmount, hasCurrency, _result$error, resultPromise, result, _this$options$loading9, _this$options$loading10;
1245
+ var _this8 = this;
1246
+ var _this$options$loading7, _this$options$loading8, isApplePay, _ref5, onBeforeConfirm, serializableParams, startTime, _yield$executeWithTim2, shouldContinue, timedOut, hasBeforeConfirmForApplePay, hasAmount, hasCurrency, cleanups, _result$error, resultPromise, result, _this$options$loading9, _this$options$loading10;
1192
1247
  return _regeneratorRuntime().wrap(function _callee6$(_context6) {
1193
1248
  while (1) switch (_context6.prev = _context6.next) {
1194
1249
  case 0:
@@ -1207,10 +1262,25 @@ export var ModernElementController = /*#__PURE__*/function () {
1207
1262
  }
1208
1263
  });
1209
1264
  case 3:
1265
+ if (!this.isSubmitting) {
1266
+ _context6.next = 6;
1267
+ break;
1268
+ }
1269
+ trackError('submit_in_progress', {});
1270
+ return _context6.abrupt("return", {
1271
+ status: 'FAIL',
1272
+ error: {
1273
+ code: 'UI_STATE_ERROR',
1274
+ message: 'submit already in progress'
1275
+ }
1276
+ });
1277
+ case 6:
1278
+ this.isSubmitting = true;
1279
+ _context6.prev = 7;
1210
1280
  isApplePay = this.currentPaymentMethodType === 'APPLEPAY'; // 剥除 onBeforeConfirm 函数,避免 postMessage 序列化报错
1211
- _ref7 = params || {}, onBeforeConfirm = _ref7.onBeforeConfirm, serializableParams = _objectWithoutProperties(_ref7, _excluded2); // ── 非 Apple Pay:同步执行 onBeforeConfirm(submit 前拦截,对齐老 ElementController) ──
1281
+ _ref5 = params || {}, onBeforeConfirm = _ref5.onBeforeConfirm, serializableParams = _objectWithoutProperties(_ref5, _excluded2); // ── 非 Apple Pay:同步执行 onBeforeConfirm(submit 前拦截,对齐老 ElementController) ──
1212
1282
  if (!(onBeforeConfirm && !isApplePay)) {
1213
- _context6.next = 26;
1283
+ _context6.next = 31;
1214
1284
  break;
1215
1285
  }
1216
1286
  logger.logInfo({
@@ -1219,18 +1289,18 @@ export var ModernElementController = /*#__PURE__*/function () {
1219
1289
  paymentMethodType: this.currentPaymentMethodType || 'UNKNOWN',
1220
1290
  path: 'nonApplePay_sync'
1221
1291
  });
1222
- _context6.prev = 7;
1292
+ _context6.prev = 12;
1223
1293
  startTime = Date.now();
1224
- _context6.next = 11;
1294
+ _context6.next = 16;
1225
1295
  return executeWithTimeout(function () {
1226
1296
  return onBeforeConfirm();
1227
1297
  });
1228
- case 11:
1298
+ case 16:
1229
1299
  _yield$executeWithTim2 = _context6.sent;
1230
1300
  shouldContinue = _yield$executeWithTim2.result;
1231
1301
  timedOut = _yield$executeWithTim2.timedOut;
1232
1302
  if (!(timedOut || !shouldContinue || Date.now() - startTime >= BEFORE_CONFIRM_TIMEOUT_MS)) {
1233
- _context6.next = 17;
1303
+ _context6.next = 22;
1234
1304
  break;
1235
1305
  }
1236
1306
  if (timedOut) {
@@ -1255,18 +1325,18 @@ export var ModernElementController = /*#__PURE__*/function () {
1255
1325
  message: 'Merchant canceled payment.'
1256
1326
  }
1257
1327
  });
1258
- case 17:
1328
+ case 22:
1259
1329
  logger.logInfo({
1260
1330
  title: 'sdk_before_confirm_success'
1261
1331
  }, {
1262
1332
  paymentMethodType: this.currentPaymentMethodType || 'UNKNOWN',
1263
1333
  path: 'nonApplePay_sync'
1264
1334
  });
1265
- _context6.next = 24;
1335
+ _context6.next = 29;
1266
1336
  break;
1267
- case 20:
1268
- _context6.prev = 20;
1269
- _context6.t0 = _context6["catch"](7);
1337
+ case 25:
1338
+ _context6.prev = 25;
1339
+ _context6.t0 = _context6["catch"](12);
1270
1340
  logger.logError({
1271
1341
  title: 'sdk_before_confirm_error'
1272
1342
  }, {
@@ -1281,10 +1351,10 @@ export var ModernElementController = /*#__PURE__*/function () {
1281
1351
  message: 'Merchant canceled payment.'
1282
1352
  }
1283
1353
  });
1284
- case 24:
1285
- _context6.next = 27;
1354
+ case 29:
1355
+ _context6.next = 32;
1286
1356
  break;
1287
- case 26:
1357
+ case 31:
1288
1358
  if (!onBeforeConfirm) {
1289
1359
  logger.logInfo({
1290
1360
  title: 'sdk_before_confirm_skip'
@@ -1293,7 +1363,7 @@ export var ModernElementController = /*#__PURE__*/function () {
1293
1363
  path: isApplePay ? 'applePay_eventBridge' : 'nonApplePay_sync'
1294
1364
  });
1295
1365
  }
1296
- case 27:
1366
+ case 32:
1297
1367
  // Apple Pay: 标记是否有 beforeConfirm,供 SUBMIT 消息的 hasBeforeConfirm 字段使用
1298
1368
  hasBeforeConfirmForApplePay = isApplePay && !!onBeforeConfirm; // ── amount/currency 校验 + 缓存(对齐老 ElementController 逻辑) ──
1299
1369
  hasAmount = !!(serializableParams !== null && serializableParams !== void 0 && serializableParams.amount);
@@ -1313,24 +1383,28 @@ export var ModernElementController = /*#__PURE__*/function () {
1313
1383
  trackElement('submit_start', {
1314
1384
  d1: this.notRedirectAfterComplete ? 'if_required' : 'always'
1315
1385
  });
1316
- _context6.prev = 35;
1386
+ // 收集本轮 once 监听的 cleanup:submitPayment 每次注册四类监听,但一轮只会命中部分,
1387
+ // 未命中的 once wrapper 绑着旧闭包(旧 onBeforeConfirm / 旧 resolve)会残留并串扰后续提交,
1388
+ // 在 submit 终态统一释放(二次 off 幂等,已命中的 once 已自行 off)。
1389
+ cleanups = [];
1390
+ _context6.prev = 41;
1317
1391
  resultPromise = new Promise(function (resolve) {
1318
1392
  // 先注册事件监听,再发送 SUBMIT,避免 webapp 快速响应导致消息丢失
1319
1393
  // PC 非 Safari:webapp 委托 SDK 拉起 Apple Pay 拿 token
1320
- _this8.bridge.once(AMSMessageType.GET_APPLE_PAY_TOKEN, function (payload) {
1394
+ cleanups.push(_this8.bridge.once(AMSMessageType.GET_APPLE_PAY_TOKEN, function (payload) {
1321
1395
  _this8.handleGetApplePayToken(payload, onBeforeConfirm).then(function () {
1322
1396
  // token 已回传 webapp,继续等 SUBMIT_REPLY
1323
1397
  });
1324
- });
1398
+ }));
1325
1399
  // Web element GP:webapp 委托 SDK/父 frame 调 loadPaymentData 拿 token
1326
- _this8.bridge.once(AMSMessageType.GET_GOOGLE_PAY_TOKEN, function (payload) {
1400
+ cleanups.push(_this8.bridge.once(AMSMessageType.GET_GOOGLE_PAY_TOKEN, function (payload) {
1327
1401
  _this8.handleGetGooglePayToken(payload);
1328
- });
1402
+ }));
1329
1403
  // Safari:webapp 请求执行商户 onBeforeConfirm 回调
1330
- _this8.bridge.once(AMSMessageType.BEFORE_CONFIRM, function () {
1404
+ cleanups.push(_this8.bridge.once(AMSMessageType.BEFORE_CONFIRM, function () {
1331
1405
  _this8.handleBeforeConfirmInternal(onBeforeConfirm || null);
1332
- });
1333
- _this8.bridge.once(MessageType.SUBMIT_REPLY, resolve);
1406
+ }));
1407
+ cleanups.push(_this8.bridge.once(MessageType.SUBMIT_REPLY, resolve));
1334
1408
  }); // 监听注册完成后再发送 SUBMIT,确保不会遗漏快速响应
1335
1409
  this.send(MessageType.SUBMIT, _objectSpread(_objectSpread({}, serializableParams), {}, {
1336
1410
  redirect: this.notRedirectAfterComplete ? 'if_required' : 'always',
@@ -1339,32 +1413,43 @@ export var ModernElementController = /*#__PURE__*/function () {
1339
1413
  amount: serializableParams.amount,
1340
1414
  currency: serializableParams.currency
1341
1415
  } : {}));
1342
- _context6.next = 40;
1416
+ _context6.next = 46;
1343
1417
  return resultPromise;
1344
- case 40:
1418
+ case 46:
1345
1419
  result = _context6.sent;
1346
1420
  trackElement('submit_reply', {
1347
1421
  d1: (result === null || result === void 0 ? void 0 : result.status) || '',
1348
1422
  d2: (result === null || result === void 0 || (_result$error = result.error) === null || _result$error === void 0 ? void 0 : _result$error.code) || ''
1349
1423
  });
1350
1424
  return _context6.abrupt("return", adaptSubmitResult(result));
1351
- case 45:
1352
- _context6.prev = 45;
1353
- _context6.t1 = _context6["catch"](35);
1425
+ case 51:
1426
+ _context6.prev = 51;
1427
+ _context6.t1 = _context6["catch"](41);
1354
1428
  trackError('submit_error', {
1355
1429
  d1: (_context6.t1 === null || _context6.t1 === void 0 ? void 0 : _context6.t1.code) || 'UNKNOWN',
1356
1430
  d2: (_context6.t1 === null || _context6.t1 === void 0 ? void 0 : _context6.t1.message) || ''
1357
1431
  });
1358
1432
  throw _context6.t1;
1359
- case 49:
1360
- _context6.prev = 49;
1433
+ case 55:
1434
+ _context6.prev = 55;
1435
+ cleanups.forEach(function (fn) {
1436
+ try {
1437
+ fn();
1438
+ } catch (_unused4) {
1439
+ /* ignore cleanup error */
1440
+ }
1441
+ });
1361
1442
  (_this$options$loading9 = this.options.loading) === null || _this$options$loading9 === void 0 || (_this$options$loading10 = _this$options$loading9.onEndLoading) === null || _this$options$loading10 === void 0 || _this$options$loading10.call(_this$options$loading9);
1362
- return _context6.finish(49);
1363
- case 52:
1443
+ return _context6.finish(55);
1444
+ case 59:
1445
+ _context6.prev = 59;
1446
+ this.isSubmitting = false;
1447
+ return _context6.finish(59);
1448
+ case 62:
1364
1449
  case "end":
1365
1450
  return _context6.stop();
1366
1451
  }
1367
- }, _callee6, this, [[7, 20], [35, 45, 49, 52]]);
1452
+ }, _callee6, this, [[7,, 59, 62], [12, 25], [41, 51, 55, 59]]);
1368
1453
  }));
1369
1454
  function submitPayment(_x8) {
1370
1455
  return _submitPayment.apply(this, arguments);
@@ -1453,8 +1538,7 @@ export var ModernElementController = /*#__PURE__*/function () {
1453
1538
  trackElement('destroy', {
1454
1539
  d1: this.status
1455
1540
  });
1456
- if (this.status !== 'ready' && this.status !== 'mounted' && this.status !== 'mounting') {
1457
- debug('INFO', 'SDK_USAGE', 'destroy', "destroy called when status is ".concat(this.status, ", cannot destroy, only destroy when status is 'ready', 'mounted' or 'mounting'"));
1541
+ if (this.status === 'initialized' || this.status === 'destroyed') {
1458
1542
  return;
1459
1543
  }
1460
1544
  this.handleCloseModal();
@@ -1462,6 +1546,7 @@ export var ModernElementController = /*#__PURE__*/function () {
1462
1546
  if ((_this$iframe = this.iframe) !== null && _this$iframe !== void 0 && _this$iframe.parentNode) this.iframe.parentNode.removeChild(this.iframe);
1463
1547
  this.iframe = null;
1464
1548
  this.setStatus('destroyed');
1549
+ this.runDestroyCallbacks();
1465
1550
  this.cachedSend = [];
1466
1551
  }
1467
1552
  }, {
@@ -1477,11 +1562,38 @@ export var ModernElementController = /*#__PURE__*/function () {
1477
1562
  key: "setStatus",
1478
1563
  value: function setStatus(status) {
1479
1564
  var _this$onStatusChange;
1480
- if (status === this.status) return;
1565
+ // Mount promises and timers may settle after destroy, but destroyed is terminal.
1566
+ if (this.status === 'destroyed' || status === this.status) return;
1481
1567
  var previousStatus = this.status;
1482
1568
  this.status = status;
1483
1569
  (_this$onStatusChange = this.onStatusChange) === null || _this$onStatusChange === void 0 || _this$onStatusChange.call(this, status, previousStatus);
1484
1570
  }
1571
+ }, {
1572
+ key: "onDestroy",
1573
+ value: function onDestroy(callback) {
1574
+ var _this10 = this;
1575
+ if (this.status === 'destroyed') {
1576
+ callback();
1577
+ return function () {
1578
+ return undefined;
1579
+ };
1580
+ }
1581
+ this.destroyCallbacks.push(callback);
1582
+ return function () {
1583
+ _this10.destroyCallbacks = _this10.destroyCallbacks.filter(function (item) {
1584
+ return item !== callback;
1585
+ });
1586
+ };
1587
+ }
1588
+ }, {
1589
+ key: "runDestroyCallbacks",
1590
+ value: function runDestroyCallbacks() {
1591
+ var callbacks = this.destroyCallbacks;
1592
+ this.destroyCallbacks = [];
1593
+ callbacks.forEach(function (callback) {
1594
+ return callback();
1595
+ });
1596
+ }
1485
1597
 
1486
1598
  /**
1487
1599
  * 带缓存的消息发送:当 status 不是 ready 时,缓存消息;ready 后由 flushCachedMessages 批量发送
@@ -1508,12 +1620,12 @@ export var ModernElementController = /*#__PURE__*/function () {
1508
1620
  }, {
1509
1621
  key: "flushCachedMessages",
1510
1622
  value: function flushCachedMessages() {
1511
- var _this10 = this;
1623
+ var _this11 = this;
1512
1624
  if (this.cachedSend.length > 0) {
1513
- this.cachedSend.forEach(function (_ref8) {
1514
- var type = _ref8.type,
1515
- payload = _ref8.payload;
1516
- _this10.bridge.send(type, payload);
1625
+ this.cachedSend.forEach(function (_ref6) {
1626
+ var type = _ref6.type,
1627
+ payload = _ref6.payload;
1628
+ _this11.bridge.send(type, payload);
1517
1629
  });
1518
1630
  this.cachedSend = [];
1519
1631
  }
@@ -1536,7 +1648,7 @@ export var ModernElementController = /*#__PURE__*/function () {
1536
1648
  var _payload$appearance,
1537
1649
  _themeColorMap$appear,
1538
1650
  _payload$appearance2,
1539
- _this11 = this;
1651
+ _this12 = this;
1540
1652
  // 清理已存在的 modal
1541
1653
  this.handleCloseModal();
1542
1654
  var mode = device.isMobile ? 'mobile' : 'desktop';
@@ -1582,7 +1694,7 @@ export var ModernElementController = /*#__PURE__*/function () {
1582
1694
  onClose: payload.closeConfirm ? function () {
1583
1695
  return new Promise(function (resolve) {
1584
1696
  // closeConfirm 时等待确认关闭(由 CLOSE_MODAL 消息触发 resolve)
1585
- _this11._closeConfirmResolve = resolve;
1697
+ _this12._closeConfirmResolve = resolve;
1586
1698
  });
1587
1699
  } : undefined
1588
1700
  })),
@@ -1601,7 +1713,7 @@ export var ModernElementController = /*#__PURE__*/function () {
1601
1713
  if (iframeOrigin && iframeOrigin !== 'null') {
1602
1714
  modalBridge.setOrigin(iframeOrigin);
1603
1715
  }
1604
- } catch (_unused4) {
1716
+ } catch (_unused5) {
1605
1717
  // URL 解析失败时保持默认 origin
1606
1718
  }
1607
1719
  // 如果 payload 有 appearance,通过 TRANSFER_MODAL_APPEARANCE 消息发送给 modal