@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.
- package/esm2022/lib/core/payment/status/check-status/check-status.service.mjs +19 -13
- package/esm2022/lib/core/payment/status/listen-status/listen-status.service.mjs +31 -1
- package/esm2022/lib/core/payment/status/status.service.mjs +48 -5
- package/esm2022/lib/core/payment/status/websocket-status/websocket-status.service.mjs +6 -2
- package/esm2022/lib/version.mjs +2 -2
- package/fesm2022/xsolla-payment-client-core.mjs +99 -17
- package/fesm2022/xsolla-payment-client-core.mjs.map +1 -1
- package/lib/core/payment/status/check-status/check-status.service.d.ts +7 -0
- package/lib/core/payment/status/listen-status/listen-status.service.d.ts +1 -0
- package/lib/core/payment/status/status.service.d.ts +13 -1
- package/lib/version.d.ts +1 -1
- package/package.json +1 -1
|
@@ -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.
|
|
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
|
-
|
|
6219
|
-
|
|
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.
|
|
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 }); }
|