@xsolla/payment-client-core 0.4.0 → 0.5.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.
@@ -13,7 +13,7 @@ import { CommonModule } from '@angular/common';
13
13
  import { provideTranslateHttpLoader } from '@ngx-translate/http-loader';
14
14
 
15
15
  // eslint-disable-next-line @typescript-eslint/naming-convention
16
- const PAYMENT_CLIENT_CORE_VERSION = '0.4.0';
16
+ const PAYMENT_CLIENT_CORE_VERSION = '0.5.0';
17
17
 
18
18
  class TranslateHelper {
19
19
  static init(service) {
@@ -6215,22 +6215,28 @@ class CheckStatusService extends XpsApiPart {
6215
6215
  this.invoice = invoice;
6216
6216
  }
6217
6217
  request() {
6218
- const requestParams = this.requestFactory(this.settingsService.token, this.invoice);
6219
- this.xpsApiService
6220
- .directPaymentRequest(requestParams)
6221
- .then((xpsResponse) => {
6222
- const response = this.xpsResponseErrorsMappingService.mapErrors(xpsResponse);
6223
- const checkStatusResponse = this.responseFactory({
6224
- status: response.status,
6225
- final: response.final,
6226
- isPaymentAccountAttached: response.is_payment_account_attached,
6227
- });
6228
- this.emitResponse(checkStatusResponse);
6229
- })
6218
+ this.requestStatus(this.invoice)
6219
+ .then((checkStatusResponse) => this.emitResponse(checkStatusResponse))
6230
6220
  .catch((error) => {
6231
6221
  throw new ResponseError(error.message);
6232
6222
  });
6233
6223
  }
6224
+ /**
6225
+ * Performs a one-shot getstatus poll and returns the mapped response without
6226
+ * emitting it to the shared `response$` stream. Used by StatusService to
6227
+ * consult the authoritative terminal signal without interfering with the
6228
+ * listen-status polling loop. See PAYMENTS-29021.
6229
+ */
6230
+ async requestStatus(invoice) {
6231
+ const requestParams = this.requestFactory(this.settingsService.token, invoice);
6232
+ const xpsResponse = await this.xpsApiService.directPaymentRequest(requestParams);
6233
+ const response = this.xpsResponseErrorsMappingService.mapErrors(xpsResponse);
6234
+ return this.responseFactory({
6235
+ status: response.status,
6236
+ final: response.final,
6237
+ isPaymentAccountAttached: response.is_payment_account_attached,
6238
+ });
6239
+ }
6234
6240
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: CheckStatusService, deps: [{ token: XpsApiService }, { token: SettingsService }, { token: XpsResponseErrorsMappingService }, { token: checkStatusRequestFactoryToken }, { token: checkStatusResponseFactoryToken }], target: i0.ɵɵFactoryTarget.Injectable }); }
6235
6241
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: CheckStatusService, providedIn: 'root' }); }
6236
6242
  }
@@ -6703,8 +6709,12 @@ class WebsocketStatusService {
6703
6709
  this.clearReinsuranceCheckTimeout();
6704
6710
  }
6705
6711
  if (message === WebSocketStatus.troubledStatus) {
6712
+ // A troubled status can be the terminal AFS-declined/refunded outcome.
6713
+ // Trigger a getstatus poll so listen-status can read the authoritative
6714
+ // verdict and finalize, instead of silently closing and leaving the UI
6715
+ // stuck on "Processing"
6706
6716
  this.clearReinsuranceCheckTimeout();
6707
- this.closeWebSocket();
6717
+ this.needCheckStatusSubject.next();
6708
6718
  }
6709
6719
  }
6710
6720
  removeSubscription() {
@@ -6740,6 +6750,7 @@ class ListenStatusService {
6740
6750
  this.destroy$ = new Subject();
6741
6751
  this.listenInvoice = null;
6742
6752
  this.websocketInvoice = null;
6753
+ this.pendingTerminalStatus = null;
6743
6754
  }
6744
6755
  listen(invoice) {
6745
6756
  if (this.isListening) {
@@ -6758,6 +6769,7 @@ class ListenStatusService {
6758
6769
  this.isListening = false;
6759
6770
  this.listenInvoice = null;
6760
6771
  this.websocketInvoice = null;
6772
+ this.pendingTerminalStatus = null;
6761
6773
  this.websocketStatusService.closeWebSocket();
6762
6774
  this.destroy$.next();
6763
6775
  this.destroy$.complete();
@@ -6774,6 +6786,13 @@ class ListenStatusService {
6774
6786
  if (!statusResponse?.status) {
6775
6787
  return;
6776
6788
  }
6789
+ const terminalStatus = this.pendingTerminalStatus;
6790
+ if (terminalStatus) {
6791
+ statusResponse.status.statusState = terminalStatus;
6792
+ if (terminalStatus === StatusEnum.canceled) {
6793
+ statusResponse.status.isCanceled = true;
6794
+ }
6795
+ }
6777
6796
  const statusSettings = {
6778
6797
  statusState: statusResponse.status.statusState,
6779
6798
  isCancelUser: statusResponse.status.isCancelUser,
@@ -6800,6 +6819,14 @@ class ListenStatusService {
6800
6819
  instanceId: statusResponse.status.pid,
6801
6820
  });
6802
6821
  }
6822
+ // A terminal verdict from the getstatus poll (e.g. AFS-declined and
6823
+ // refunded) is authoritative: finalize even when the full status still
6824
+ // reports needToCheck.
6825
+ if (terminalStatus) {
6826
+ this.stopListening();
6827
+ this.statusDoneSubject.next(statusSettings);
6828
+ return;
6829
+ }
6803
6830
  if (statusSettings.needWebSocket) {
6804
6831
  if (!this.websocketStatusService.isOpened ||
6805
6832
  this.websocketInvoice !== invoice) {
@@ -6826,6 +6853,19 @@ class ListenStatusService {
6826
6853
  this.websocketStatusService.closeWebSocket();
6827
6854
  return;
6828
6855
  }
6856
+ // AFS-declined / refunded transactions report a terminal failure here
6857
+ // (canceled/error + final) while the full status may still say
6858
+ // processing. Re-fetch the rich status, then flag it so the update
6859
+ // handler overrides the state to the terminal verdict and finalizes.
6860
+ const isTerminalFailure = checkStatusResponse.final === true &&
6861
+ (checkStatusResponse.status === StatusEnum.canceled ||
6862
+ checkStatusResponse.status === StatusEnum.error);
6863
+ if (isTerminalFailure) {
6864
+ this.pendingTerminalStatus = checkStatusResponse.status;
6865
+ this.statusService.request();
6866
+ this.websocketStatusService.closeWebSocket();
6867
+ return;
6868
+ }
6829
6869
  if (checkStatusResponse.final === false) {
6830
6870
  this.websocketStatusService.startReinsuranceCheckTimeout();
6831
6871
  return;
@@ -7019,13 +7059,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
7019
7059
  }], ctorParameters: () => [{ type: StatusRequestService }, { type: ListenStatusService }, { type: SavedPaymentAccountChangesService }, { type: NextActionService }, { type: CheckStatusService }, { type: StatusUpdatedActionCreator }] });
7020
7060
 
7021
7061
  class StatusService {
7022
- constructor(urlService, statusRequestService, financeDetailsService, listenStatusService, nextActionService, savePaymentMethodStatusService, statusUpdatedActionCreator) {
7062
+ constructor(urlService, statusRequestService, financeDetailsService, listenStatusService, nextActionService, savePaymentMethodStatusService, checkStatusService, statusUpdatedActionCreator) {
7023
7063
  this.urlService = urlService;
7024
7064
  this.statusRequestService = statusRequestService;
7025
7065
  this.financeDetailsService = financeDetailsService;
7026
7066
  this.listenStatusService = listenStatusService;
7027
7067
  this.nextActionService = nextActionService;
7028
7068
  this.savePaymentMethodStatusService = savePaymentMethodStatusService;
7069
+ this.checkStatusService = checkStatusService;
7029
7070
  this.statusUpdatedActionCreator = statusUpdatedActionCreator;
7030
7071
  this.listenStatusSubscription = null;
7031
7072
  }
@@ -7038,6 +7079,7 @@ class StatusService {
7038
7079
  this.startWaitingStatusUpdates(params.invoice);
7039
7080
  this.statusRequestService.request();
7040
7081
  const statusResponse = await firstValueFrom(this.statusRequestService.response$);
7082
+ await this.applyPollTerminalStatus(statusResponse.status, params.invoice);
7041
7083
  this.financeDetailsService.emitWithStatus(statusResponse.status);
7042
7084
  return statusResponse.status;
7043
7085
  }
@@ -7056,6 +7098,46 @@ class StatusService {
7056
7098
  this.listenStatusSubscription = null;
7057
7099
  this.listenStatusService.stopListening();
7058
7100
  }
7101
+ /**
7102
+ * The full-status endpoint can report a non-terminal state (e.g.
7103
+ * `processing` with group `delivering`) for a transaction that has actually
7104
+ * been terminated — most notably a card payment declined by AFS and then
7105
+ * auto-refunded. In that scenario the lightweight getstatus poll is the only
7106
+ * authoritative terminal signal (`status: canceled`, `final: true`), so we
7107
+ * consult it and surface the terminal verdict to the partner instead of
7108
+ * leaving `statusState` stuck on `processing`.
7109
+ */
7110
+ async applyPollTerminalStatus(status, invoice) {
7111
+ const nonTerminalStates = [
7112
+ StatusEnum.created,
7113
+ StatusEnum.awaiting,
7114
+ StatusEnum.processing,
7115
+ StatusEnum.held,
7116
+ ];
7117
+ if (!status.needToCheck ||
7118
+ !nonTerminalStates.includes(status.statusState)) {
7119
+ return;
7120
+ }
7121
+ let pollStatus;
7122
+ try {
7123
+ pollStatus = await this.checkStatusService.requestStatus(invoice);
7124
+ }
7125
+ catch {
7126
+ return;
7127
+ }
7128
+ const terminalStatus = pollStatus.final === true &&
7129
+ (pollStatus.status === StatusEnum.canceled ||
7130
+ pollStatus.status === StatusEnum.error)
7131
+ ? pollStatus.status
7132
+ : null;
7133
+ if (!terminalStatus) {
7134
+ return;
7135
+ }
7136
+ status.statusState = terminalStatus;
7137
+ if (terminalStatus === StatusEnum.canceled) {
7138
+ status.isCanceled = true;
7139
+ }
7140
+ }
7059
7141
  getRequestParamsFromUrl(url) {
7060
7142
  const searchParams = this.urlService.getSearchParams(url ?? '');
7061
7143
  const keys = new Set(Object.keys(searchParams));
@@ -7091,7 +7173,7 @@ class StatusService {
7091
7173
  this.nextActionService.emitFlowUpdates(this.statusUpdatedActionCreator.getActionData({ statusResponse }));
7092
7174
  });
7093
7175
  }
7094
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: StatusService, deps: [{ token: UrlService }, { token: StatusRequestService }, { token: FinanceDetailsService }, { token: ListenStatusService }, { token: NextActionService }, { token: SavePaymentMethodStatusService }, { token: StatusUpdatedActionCreator }], target: i0.ɵɵFactoryTarget.Injectable }); }
7176
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: StatusService, deps: [{ token: UrlService }, { token: StatusRequestService }, { token: FinanceDetailsService }, { token: ListenStatusService }, { token: NextActionService }, { token: SavePaymentMethodStatusService }, { token: CheckStatusService }, { token: StatusUpdatedActionCreator }], target: i0.ɵɵFactoryTarget.Injectable }); }
7095
7177
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: StatusService, providedIn: 'root' }); }
7096
7178
  }
7097
7179
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: StatusService, decorators: [{
@@ -7099,7 +7181,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
7099
7181
  args: [{
7100
7182
  providedIn: 'root',
7101
7183
  }]
7102
- }], ctorParameters: () => [{ type: UrlService }, { type: StatusRequestService }, { type: FinanceDetailsService }, { type: ListenStatusService }, { type: NextActionService }, { type: SavePaymentMethodStatusService }, { type: StatusUpdatedActionCreator }] });
7184
+ }], ctorParameters: () => [{ type: UrlService }, { type: StatusRequestService }, { type: FinanceDetailsService }, { type: ListenStatusService }, { type: NextActionService }, { type: SavePaymentMethodStatusService }, { type: CheckStatusService }, { type: StatusUpdatedActionCreator }] });
7103
7185
 
7104
7186
  class CreditCardStateService extends EmitDataService {
7105
7187
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: CreditCardStateService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }