@xsolla/payment-client-core 0.1.1 → 0.1.3

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.
Files changed (50) hide show
  1. package/esm2020/lib/core/payment/actions/next-action.interface.mjs +1 -1
  2. package/esm2020/lib/core/payment/actions/next-actions.mjs +1 -3
  3. package/esm2020/lib/core/payment/actions/show-fields/show-fields.action.token.mjs +3 -2
  4. package/esm2020/lib/core/payment/form/converters/converter.abstract.mjs +8 -6
  5. package/esm2020/lib/core/payment/form/converters/email.converter.mjs +3 -3
  6. package/esm2020/lib/core/payment/form/converters/zip.converter.mjs +4 -5
  7. package/esm2020/lib/core/payment/form/field-status.interface.mjs +2 -0
  8. package/esm2020/lib/core/payment/form/form-fields-status.interface.mjs +2 -0
  9. package/esm2020/lib/core/payment/form/input-event.enum.mjs +6 -0
  10. package/esm2020/lib/core/payment/form/validators/async-validator-fn.type.mjs +2 -0
  11. package/esm2020/lib/core/payment/form/validators/convert.function.mjs +6 -14
  12. package/esm2020/lib/core/payment/form/validators/email-validator.mjs +3 -4
  13. package/esm2020/lib/core/payment/form/validators/required-validator.mjs +3 -4
  14. package/esm2020/lib/core/payment/form/validators/validation-error.interface.mjs +1 -1
  15. package/esm2020/lib/core/payment/payment.interface.mjs +1 -1
  16. package/esm2020/lib/core/payment/payment.service.mjs +52 -23
  17. package/esm2020/lib/core/payment/sync/sync-response.class.mjs +7 -4
  18. package/esm2020/lib/core/payment/sync/sync.service.mjs +4 -10
  19. package/esm2020/lib/core-library.service.mjs +3 -1
  20. package/fesm2015/xsolla-payment-client-core.mjs +87 -87
  21. package/fesm2015/xsolla-payment-client-core.mjs.map +1 -1
  22. package/fesm2020/xsolla-payment-client-core.mjs +86 -87
  23. package/fesm2020/xsolla-payment-client-core.mjs.map +1 -1
  24. package/lib/core/payment/actions/next-action.interface.d.ts +1 -2
  25. package/lib/core/payment/form/converters/converter.abstract.d.ts +4 -1
  26. package/lib/core/payment/form/converters/email.converter.d.ts +1 -1
  27. package/lib/core/payment/form/field-status.interface.d.ts +7 -0
  28. package/lib/core/payment/form/form-fields-status.interface.d.ts +4 -0
  29. package/lib/core/payment/form/input-event.enum.d.ts +4 -0
  30. package/lib/core/payment/form/validators/async-validator-fn.type.d.ts +4 -0
  31. package/lib/core/payment/form/validators/convert.function.d.ts +1 -1
  32. package/lib/core/payment/form/validators/email-validator.d.ts +1 -1
  33. package/lib/core/payment/form/validators/required-validator.d.ts +1 -1
  34. package/lib/core/payment/form/validators/validation-error.interface.d.ts +0 -1
  35. package/lib/core/payment/payment.interface.d.ts +5 -1
  36. package/lib/core/payment/payment.service.d.ts +10 -2
  37. package/lib/core/payment/sync/sync-response.class.d.ts +2 -4
  38. package/package.json +1 -1
  39. package/xsolla-payment-client-core-0.1.3.tgz +0 -0
  40. package/esm2020/lib/core/payment/actions/show-field-state/show-field-state.action.class.mjs +0 -11
  41. package/esm2020/lib/core/payment/actions/show-field-state/show-field-state.action.token.mjs +0 -13
  42. package/esm2020/lib/core/payment/actions/show-field-state/show-field-state.action.type.mjs +0 -2
  43. package/esm2020/lib/core/payment/form/validators/errors-codes.enum.mjs +0 -8
  44. package/esm2020/lib/core/payment/show-field-state.interface.mjs +0 -2
  45. package/lib/core/payment/actions/show-field-state/show-field-state.action.class.d.ts +0 -8
  46. package/lib/core/payment/actions/show-field-state/show-field-state.action.token.d.ts +0 -4
  47. package/lib/core/payment/actions/show-field-state/show-field-state.action.type.d.ts +0 -10
  48. package/lib/core/payment/form/validators/errors-codes.enum.d.ts +0 -6
  49. package/lib/core/payment/show-field-state.interface.d.ts +0 -7
  50. package/xsolla-payment-client-core-0.1.1.tgz +0 -0
@@ -573,16 +573,11 @@ const isSubmitResponse = (value) => {
573
573
  return value instanceof SubmitResponse;
574
574
  };
575
575
 
576
- class ShowFieldStateAction {
577
- constructor(control) {
578
- this.type = 'show_field_state';
579
- this.data = {
580
- fieldName: control.fieldName,
581
- fieldState: control.fieldState,
582
- error: control.error,
583
- };
584
- }
585
- }
576
+ var InputEventName;
577
+ (function (InputEventName) {
578
+ InputEventName["blur"] = "blur";
579
+ InputEventName["focus"] = "focus";
580
+ })(InputEventName || (InputEventName = {}));
586
581
 
587
582
  class InitializeResponse {
588
583
  constructor(response) {
@@ -664,7 +659,8 @@ const showFieldsActionFactoryProvider = {
664
659
  useValue: {
665
660
  factory: (...args) => new ShowFieldsAction(...args),
666
661
  isSuitable: (response) => {
667
- return response.currentCommand === XpsCommand.check;
662
+ return (response.currentCommand === XpsCommand.check ||
663
+ response.currentCommand === XpsCommand.form);
668
664
  },
669
665
  },
670
666
  };
@@ -762,17 +758,6 @@ const showErrorsActionFactoryProvider = {
762
758
  },
763
759
  };
764
760
 
765
- const showFieldStateActionFactoryToken = new InjectionToken('ShowFieldStateAction');
766
- const showFieldStateActionFactoryProvider = {
767
- provide: showFieldStateActionFactoryToken,
768
- useValue: {
769
- factory: (...args) => new ShowFieldStateAction(...args),
770
- isSuitable: () => {
771
- return true;
772
- },
773
- },
774
- };
775
-
776
761
  const nextActionsToken = new InjectionToken('Action');
777
762
  const nextActions = [
778
763
  showFieldsActionFactoryProvider,
@@ -782,7 +767,6 @@ const nextActions = [
782
767
  const flowUpdateNextActions = [
783
768
  statusUpdatedActionFactoryProvider,
784
769
  showErrorsActionFactoryProvider,
785
- showFieldStateActionFactoryProvider,
786
770
  ];
787
771
 
788
772
  class NextActionService {
@@ -1210,11 +1194,14 @@ const syncRequestFactoryProvider = {
1210
1194
 
1211
1195
  class SyncResponse {
1212
1196
  constructor(changedField, response) {
1197
+ this.error = null;
1213
1198
  const fieldError = response?.errors?.find((error) => error.element_name === changedField.name);
1214
1199
  if (fieldError) {
1215
- this.rewritableError = {
1216
- code: fieldError.code,
1217
- message: fieldError.message,
1200
+ this.error = {
1201
+ asyncError: {
1202
+ code: fieldError.code,
1203
+ message: fieldError.message,
1204
+ },
1218
1205
  };
1219
1206
  }
1220
1207
  this.fields = Object.keys(response.form).map((key) => response.form[key]);
@@ -1264,24 +1251,18 @@ class SyncService extends EmitDataService {
1264
1251
  }
1265
1252
  if (prevFieldState && value === prevFieldState.value) {
1266
1253
  return Promise.resolve(prevFieldState.result).then((response) => {
1267
- return response?.rewritableError ?? null;
1254
+ return response?.error ?? null;
1268
1255
  });
1269
1256
  }
1270
1257
  const requestPromise = this.request(field);
1271
1258
  this.setPrevFieldSyncState(field, value, requestPromise);
1272
1259
  return requestPromise.then((response) => {
1273
- return response.rewritableError ?? null;
1260
+ return response.error ?? null;
1274
1261
  });
1275
1262
  }
1276
1263
  getSyncValidationErrors(field) {
1277
1264
  const control = this.form.get(field.name);
1278
- if (!control) {
1279
- return null;
1280
- }
1281
- const rewritableError = control.errors?.rewritableError;
1282
- return rewritableError
1283
- ? { message: rewritableError.message, code: rewritableError.code }
1284
- : null;
1265
+ return control?.errors ?? null;
1285
1266
  }
1286
1267
  async request(field) {
1287
1268
  const requestParams = this.requestFactory({
@@ -1341,44 +1322,28 @@ class TextControlConfig extends ControlConfig {
1341
1322
  }
1342
1323
  }
1343
1324
 
1344
- function convert(validator, message, errorCode) {
1325
+ function convert(validator, message) {
1345
1326
  return (control) => {
1346
1327
  const errors = validator(control);
1347
1328
  if (!errors) {
1348
1329
  return errors;
1349
1330
  }
1350
1331
  const firstErrorKey = Object.keys(errors)[0];
1351
- let error = errors[firstErrorKey];
1352
- error = typeof error === 'object' && error !== null ? error : {};
1353
- const rewritableError = { ...error };
1354
- rewritableError.message = message;
1355
- rewritableError.code = errorCode;
1356
- if (error[firstErrorKey] != null) {
1357
- rewritableError.value = error[firstErrorKey];
1358
- }
1359
- if (rewritableError.requiredLength) {
1360
- rewritableError.number = rewritableError.requiredLength;
1361
- }
1362
- errors.rewritableError = rewritableError;
1332
+ const firstError = errors[firstErrorKey];
1333
+ const error = typeof firstError === 'object' && firstError !== null ? firstError : {};
1334
+ error.message = message;
1335
+ errors[firstErrorKey] = error;
1363
1336
  return errors;
1364
1337
  };
1365
1338
  }
1366
1339
 
1367
- var ErrorsCodes;
1368
- (function (ErrorsCodes) {
1369
- ErrorsCodes["minLength"] = "min-length";
1370
- ErrorsCodes["maxLength"] = "max-length";
1371
- ErrorsCodes["invalidValue"] = "invalid-value";
1372
- ErrorsCodes["required"] = "required";
1373
- })(ErrorsCodes || (ErrorsCodes = {}));
1374
-
1375
1340
  const emailRegexp = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i; // eslint-disable-line
1376
- function emailValidator() {
1377
- return convert(Validators.pattern(emailRegexp), 'Enter a valid email', ErrorsCodes.invalidValue);
1341
+ function convertedEmailValidator() {
1342
+ return convert(Validators.pattern(emailRegexp), 'Enter a valid email');
1378
1343
  }
1379
1344
 
1380
- function requiredValidator() {
1381
- return convert(Validators.required, 'Required', ErrorsCodes.required);
1345
+ function convertedRequiredValidator() {
1346
+ return convert(Validators.required, 'Required');
1382
1347
  }
1383
1348
 
1384
1349
  class Converter {
@@ -1393,12 +1358,12 @@ class Converter {
1393
1358
  return config;
1394
1359
  }
1395
1360
  convertToFormControl(field) {
1396
- return new UntypedFormControl(this.getValue(field), this.getValidators(field));
1361
+ return new UntypedFormControl(this.getValue(field), this.getValidators(field), this.getAsyncValidators(field));
1397
1362
  }
1398
1363
  getValidators(field) {
1399
1364
  const validators = [];
1400
1365
  if (field.isMandatory === "1" /* XpsBoolean.true */) {
1401
- validators.push(requiredValidator());
1366
+ validators.push(convertedRequiredValidator());
1402
1367
  }
1403
1368
  if (field.regex) {
1404
1369
  const match = /^\/(.*)\/([gim]*)$/.exec(field.regex);
@@ -1407,11 +1372,14 @@ class Converter {
1407
1372
  const errorMessage = field.validation_error_msg
1408
1373
  ? field.validation_error_msg
1409
1374
  : 'Enter valid data';
1410
- validators.push(convert(Validators.pattern(pattern), errorMessage, ErrorsCodes.invalidValue));
1375
+ validators.push(convert(Validators.pattern(pattern), errorMessage));
1411
1376
  }
1412
1377
  }
1413
1378
  return validators;
1414
1379
  }
1380
+ getAsyncValidators(field) {
1381
+ return async () => this.syncService.validate(field);
1382
+ }
1415
1383
  createDefaultControlConfig(controlConfigClass, field) {
1416
1384
  const config = new controlConfigClass();
1417
1385
  this.copyCommonOptions(config, field);
@@ -1455,7 +1423,7 @@ class EmailConverter extends Converter {
1455
1423
  super(syncService);
1456
1424
  }
1457
1425
  getValidators(field) {
1458
- return super.getValidators(field).concat([emailValidator()]);
1426
+ return super.getValidators(field).concat([convertedEmailValidator()]);
1459
1427
  }
1460
1428
  createControlConfig(field) {
1461
1429
  const config = this.createDefaultControlConfig(TextControlConfig, field);
@@ -1496,11 +1464,11 @@ class ZipConverter extends Converter {
1496
1464
  const validators = super
1497
1465
  .getValidators(field)
1498
1466
  .concat([
1499
- convert(Validators.minLength(minLength), `Enter at least ${minLength} characters`, ErrorsCodes.minLength),
1500
- convert(Validators.maxLength(maxLength), `Enter no more than ${maxLength} characters`, ErrorsCodes.maxLength),
1467
+ convert(Validators.minLength(minLength), `Enter at least ${minLength} characters`),
1468
+ convert(Validators.maxLength(maxLength), `Enter no more than ${maxLength} characters`),
1501
1469
  ]);
1502
1470
  if (this.isUsCountry) {
1503
- validators.push(convert(restrictedValueValidator('00000'), 'Enter a valid ZIP code', ErrorsCodes.invalidValue));
1471
+ validators.push(convert(restrictedValueValidator('00000'), 'Enter a valid ZIP code'));
1504
1472
  }
1505
1473
  return validators;
1506
1474
  }
@@ -3001,6 +2969,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.7", ngImpor
3001
2969
  }] }]; } });
3002
2970
 
3003
2971
  class PaymentService {
2972
+ get formFieldsStatus$() {
2973
+ return this.formFieldsStatusSubject.asObservable().pipe(filter$1(Boolean));
2974
+ }
3004
2975
  constructor(initializeService, submitService, syncService, formService, statusService, financeDetailsService, nextActionService, controlConfigService) {
3005
2976
  this.initializeService = initializeService;
3006
2977
  this.submitService = submitService;
@@ -3013,9 +2984,11 @@ class PaymentService {
3013
2984
  this.form = null;
3014
2985
  this.data = null;
3015
2986
  this.financeDetails = null;
2987
+ this.focusFieldName = null;
3016
2988
  this.fields = [];
3017
2989
  this.destroy$ = new Subject();
3018
2990
  this.formDestroy$ = new Subject();
2991
+ this.formFieldsStatusSubject = new BehaviorSubject(null);
3019
2992
  }
3020
2993
  async initialize(config) {
3021
2994
  this.destroy$ = new Subject();
@@ -3025,15 +2998,21 @@ class PaymentService {
3025
2998
  this.data = await this.initializeService.request(config);
3026
2999
  this.fields = this.getFields();
3027
3000
  this.form = this.formService.createForm(this.fields);
3028
- this.listenSyncValidationErrors(this.form);
3029
- this.emitFinanceDetails(this.data.paymentForm?.summary);
3030
3001
  this.setupSyncService(this.form, this.fields);
3002
+ this.listenFormStatusChanges(this.form);
3003
+ this.emitFinanceDetails(this.data.paymentForm?.summary);
3004
+ this.emitFormFieldsStatus(this.form);
3031
3005
  return this.fields.filter((field) => field?.isVisible === "1" /* XpsBoolean.true */);
3032
3006
  }
3033
3007
  async submit() {
3034
3008
  if (!this.form) {
3035
3009
  throw new Error('form is empty!');
3036
3010
  }
3011
+ if (!this.form.valid) {
3012
+ this.form.markAllAsTouched();
3013
+ this.emitFormFieldsStatus(this.form);
3014
+ return;
3015
+ }
3037
3016
  const submitResponse = await this.submitService.request(this.fields, this.form);
3038
3017
  this.data = submitResponse;
3039
3018
  this.emitFinanceDetails(submitResponse.summary);
@@ -3078,6 +3057,25 @@ class PaymentService {
3078
3057
  const formControl = this.form?.get(name);
3079
3058
  formControl?.setValue(value);
3080
3059
  }
3060
+ changeFieldState(name, state) {
3061
+ const formControl = this.form?.get(name);
3062
+ if (!this.form || !formControl) {
3063
+ return;
3064
+ }
3065
+ switch (state) {
3066
+ case InputEventName.blur: {
3067
+ formControl.markAsTouched();
3068
+ this.focusFieldName = null;
3069
+ break;
3070
+ }
3071
+ case InputEventName.focus: {
3072
+ formControl.markAsUntouched();
3073
+ this.focusFieldName = name;
3074
+ break;
3075
+ }
3076
+ }
3077
+ this.emitFormFieldsStatus(this.form);
3078
+ }
3081
3079
  destroy() {
3082
3080
  this.data = null;
3083
3081
  this.financeDetails = null;
@@ -3089,6 +3087,19 @@ class PaymentService {
3089
3087
  emitFinanceDetails(summary) {
3090
3088
  this.financeDetailsService.emit(summary);
3091
3089
  }
3090
+ emitFormFieldsStatus(form) {
3091
+ const formFieldsStatus = {};
3092
+ Object.keys(form.controls).forEach((name) => {
3093
+ const control = form.controls[name];
3094
+ formFieldsStatus[name] = {
3095
+ name: name,
3096
+ validationStatus: control.status,
3097
+ errors: control.errors,
3098
+ isFocused: this.focusFieldName === name,
3099
+ };
3100
+ });
3101
+ this.formFieldsStatusSubject.next(formFieldsStatus);
3102
+ }
3092
3103
  setupSyncService(form, fields) {
3093
3104
  this.syncService.setup(form, fields);
3094
3105
  this.syncService.data$
@@ -3100,24 +3111,10 @@ class PaymentService {
3100
3111
  .pipe(takeUntil(this.destroy$))
3101
3112
  .subscribe((financeDetails) => (this.financeDetails = financeDetails));
3102
3113
  }
3103
- listenSyncValidationErrors(form) {
3104
- Object.keys(form.controls).forEach((key) => {
3105
- const control = form.get(key);
3106
- if (control) {
3107
- control.statusChanges
3108
- .pipe(takeUntil(this.formDestroy$))
3109
- .subscribe((status) => {
3110
- const controlError = control.errors?.['rewritableError'] ??
3111
- null;
3112
- const controlObject = {
3113
- fieldName: key,
3114
- fieldState: status,
3115
- error: controlError,
3116
- };
3117
- this.nextActionService.emitFlowUpdates(new ShowFieldStateAction(controlObject));
3118
- });
3119
- }
3120
- });
3114
+ listenFormStatusChanges(form) {
3115
+ form.statusChanges
3116
+ .pipe(takeUntil(this.formDestroy$))
3117
+ .subscribe(() => this.emitFormFieldsStatus(form));
3121
3118
  }
3122
3119
  }
3123
3120
  PaymentService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.7", ngImport: i0, type: PaymentService, deps: [{ token: InitializeService }, { token: SubmitService }, { token: SyncService }, { token: FormService }, { token: StatusService }, { token: FinanceDetailsService }, { token: NextActionService }, { token: ControlConfigService }], target: i0.ɵɵFactoryTarget.Injectable });
@@ -3275,8 +3272,10 @@ class CoreLibraryService {
3275
3272
  flowUpdates$: this.nextActionService.flowUpdates$,
3276
3273
  financeDetailsUpdates$: this.financeDetailsService.data$,
3277
3274
  form: {
3275
+ fieldsStatus$: this.paymentService.formFieldsStatus$,
3278
3276
  getControlConfig: (field) => this.paymentService.getFieldConfig(field),
3279
3277
  changeFieldValue: (name, value) => this.paymentService.changeFieldValue(name, value),
3278
+ changeFieldState: (name, state) => this.paymentService.changeFieldState(name, state),
3280
3279
  },
3281
3280
  };
3282
3281
  }