@flittpayments/js-sdk 2.0.4 → 2.0.6

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.
@@ -312,9 +312,144 @@ const jsonParse = (value, defaults) => {
312
312
  return defaults;
313
313
  }
314
314
  };
315
+ const PromiseWithResolvers = () => {
316
+ let resolve;
317
+ let reject;
318
+ const promise = new Promise((promiseResolve, promiseReject) => {
319
+ resolve = promiseResolve;
320
+ reject = promiseReject;
321
+ });
322
+ return {
323
+ promise,
324
+ resolve,
325
+ reject
326
+ };
327
+ };
328
+
329
+ /**
330
+ * @typedef PaymentRequestMethod
331
+ * @property {{[string]:any}} data
332
+ */
333
+
334
+ /**
335
+ * @typedef PaymentRequestOptions
336
+ * @property {boolean} [requestPayerEmail]
337
+ * @property {boolean} [requestPayerPhone]
338
+ * @property {boolean} [requestPayerName]
339
+ * @property {boolean} [requestShipping]
340
+ */
341
+
342
+ /**
343
+ * @typedef PaymentRequestConfig
344
+ * @property [PaymentRequestMethod] methods
345
+ * @property {{[string]:any}} options
346
+ */
347
+
348
+ /**
349
+ * @param {PaymentRequestConfig} config
350
+ */
351
+ const getAppleSessionConfig = config => {
352
+ const {
353
+ methods,
354
+ details,
355
+ options
356
+ } = config;
357
+ const method = methods.find(item => item.supportedMethods === 'https://apple.com/apple-pay');
358
+ const requiredShippingContactFields = [];
359
+ const requiredBillingContactFields = [];
360
+ if (options.requestShipping) requiredBillingContactFields.push('postalAddress');
361
+ if (options.requestPayerName) requiredShippingContactFields.push('name');
362
+ if (options.requestPayerEmail) requiredShippingContactFields.push('email');
363
+ if (options.requestPayerPhone) requiredShippingContactFields.push('phone');
364
+ //method.data.recurringPaymentRequest = recurringPaymentRequest
365
+ //method.data.deferredPaymentRequest = deferredPaymentRequest
366
+ return {
367
+ version: method.data.version,
368
+ countryCode: method.data.countryCode,
369
+ supportedNetworks: method.data.supportedNetworks,
370
+ merchantCapabilities: method.data.merchantCapabilities,
371
+ merchantIdentifier: method.data.merchantIdentifier,
372
+ lineItems: method.data.lineItems || details.displayItems,
373
+ additionalLineItems: method.data.additionalLineItems,
374
+ recurringPaymentRequest: method.data.recurringPaymentRequest,
375
+ deferredPaymentRequest: method.data.deferredPaymentRequest,
376
+ //
377
+ currencyCode: method.data.currencyCode || details.total.amount.currency,
378
+ total: {
379
+ label: details.total.label,
380
+ amount: details.total.amount.value
381
+ },
382
+ //
383
+ requiredBillingContactFields,
384
+ requiredShippingContactFields
385
+ };
386
+ };
387
+
388
+ // export const deferredPaymentRequest = {
389
+ // paymentDescription: 'Бронювання готельного номеру',
390
+ // deferredBilling: {
391
+ // label: 'Бронювання',
392
+ // amount: '50.00',
393
+ // type: 'final',
394
+ // paymentTiming: 'deferred',
395
+ // deferredPaymentDate: new Date('2026-01-05'),
396
+ // },
397
+ // managementURL: 'https://flitt.ksv.app/manage-booking.html',
398
+ // }
399
+
400
+ // export const recurringPaymentRequest = {
401
+ // paymentDescription: 'Місячна підписка',
402
+ // regularBilling: {
403
+ // label: 'Підписка на місяць',
404
+ // amount: '20.00',
405
+ // type: 'final',
406
+ // paymentTiming: 'recurring',
407
+ // recurringPaymentStartDate: new Date('2026-01-01T00:00:00'),
408
+ // recurringPaymentIntervalUnit: 'month',
409
+ // recurringPaymentIntervalCount: 6,
410
+ // recurringPaymentEndDate: new Date('2028-01-01T00:00:00'),
411
+ // },
412
+ // managementURL: 'https://flitt.ksv.app/manage-subscription.html',
413
+ // }
414
+
415
+ const getFunction = path => {
416
+ const props = path.split('.');
417
+ const value = props.reduce((acc, cur) => {
418
+ if (acc && acc.hasOwnProperty(cur)) {
419
+ acc = acc[cur];
420
+ return acc;
421
+ }
422
+ return null;
423
+ }, window);
424
+ return isFunction(value) ? value : value;
425
+ };
426
+ const loadExternalApi = (url, path) => {
427
+ const [existScript] = toArray(document.scripts).filter(script => script.src === url);
428
+ if (existScript) return Promise.resolve(getFunction(path));
429
+ const {
430
+ promise,
431
+ resolve
432
+ } = PromiseWithResolvers();
433
+ const script = document.createElement('script');
434
+ script.async = true;
435
+ script.src = url;
436
+ script.addEventListener('load', () => {
437
+ const value = getFunction(path);
438
+ console.log('api loading', 'success', url);
439
+ resolve(value);
440
+ });
441
+ script.addEventListener('error', e => {
442
+ const value = getFunction(path);
443
+ console.log('api loading', 'error', url);
444
+ resolve(value);
445
+ });
446
+ document.head.appendChild(script);
447
+ return promise;
448
+ };
315
449
 
316
450
  var Utils = /*#__PURE__*/Object.freeze({
317
451
  __proto__: null,
452
+ PromiseWithResolvers: PromiseWithResolvers,
318
453
  addAttr: addAttr,
319
454
  addClass: addClass,
320
455
  cleanObject: cleanObject,
@@ -323,6 +458,8 @@ var Utils = /*#__PURE__*/Object.freeze({
323
458
  deparam: deparam,
324
459
  extend: extend,
325
460
  forEach: forEach,
461
+ getAppleSessionConfig: getAppleSessionConfig,
462
+ getFunction: getFunction,
326
463
  getPath: getPath,
327
464
  getPaymentRequest: getPaymentRequest,
328
465
  getStyle: getStyle,
@@ -340,6 +477,7 @@ var Utils = /*#__PURE__*/Object.freeze({
340
477
  isRegexp: isRegexp,
341
478
  isString: isString,
342
479
  jsonParse: jsonParse,
480
+ loadExternalApi: loadExternalApi,
343
481
  map: map,
344
482
  param: param,
345
483
  querySelector: querySelector,
@@ -370,6 +508,32 @@ const GoogleBaseRequest = {
370
508
  }
371
509
  }]
372
510
  };
511
+ const PaymentMethodsConfig = [{
512
+ url: 'https://pay.google.com/gp/p/js/pay.js',
513
+ name: 'google',
514
+ path: 'google.payments.api.PaymentsClient',
515
+ callback(PaymentsClient) {
516
+ if (PaymentsClient) {
517
+ return new PaymentsClient({
518
+ environment: 'TEST'
519
+ }).isReadyToPay(GoogleBaseRequest).catch(e => {
520
+ console.log(e);
521
+ return false;
522
+ });
523
+ }
524
+ return false;
525
+ }
526
+ }, {
527
+ url: 'https://applepay.cdn-apple.com/jsapi/1.latest/apple-pay-sdk.js',
528
+ name: 'apple',
529
+ path: 'ApplePaySession',
530
+ callback(ApplePaySession) {
531
+ if (ApplePaySession) {
532
+ return ApplePaySession.supportsVersion(3);
533
+ }
534
+ return false;
535
+ }
536
+ }];
373
537
  const GooglePayLanguages = ['ar', 'bg', 'ca', 'zh', 'hr', 'cs', 'da', 'nl', 'en', 'et', 'fi', 'fr', 'de', 'el', 'id', 'it', 'ja', 'ko', 'ms', 'no', 'pl', 'pt', 'ru', 'sr', 'sk', 'sl', 'es', 'sv', 'th', 'tr', 'uk'];
374
538
  const PaymentRequestDetails = {
375
539
  total: {
@@ -497,6 +661,7 @@ var Config = /*#__PURE__*/Object.freeze({
497
661
  GoogleBaseRequest: GoogleBaseRequest,
498
662
  GooglePayApi: GooglePayApi,
499
663
  GooglePayLanguages: GooglePayLanguages,
664
+ PaymentMethodsConfig: PaymentMethodsConfig,
500
665
  PaymentRequestDetails: PaymentRequestDetails,
501
666
  ProxyUrl: ProxyUrl
502
667
  });
@@ -551,28 +716,31 @@ function Deferred() {
551
716
  const promise = {
552
717
  done(callback) {
553
718
  if (isFunction(callback)) {
554
- if (status === RESOLVED) {
719
+ if (this.isResolved()) {
555
720
  callback.apply(resultContext, resultArgs);
721
+ } else {
722
+ resolveCallbacks.push(callback);
556
723
  }
557
- resolveCallbacks.push(callback);
558
724
  }
559
725
  return this;
560
726
  },
561
727
  fail(callback) {
562
728
  if (isFunction(callback)) {
563
- if (status === REJECTED) {
729
+ if (this.isRejected()) {
564
730
  callback.apply(resultContext, resultArgs);
731
+ } else {
732
+ rejectCallbacks.push(callback);
565
733
  }
566
- rejectCallbacks.push(callback);
567
734
  }
568
735
  return this;
569
736
  },
570
737
  progress(callback) {
571
738
  if (isFunction(callback)) {
572
- if (status === PROGRESS) {
739
+ if (this.isProgress()) {
573
740
  callback.apply(resultContext, resultArgs);
741
+ } else {
742
+ notifyCallbacks.push(callback);
574
743
  }
575
- notifyCallbacks.push(callback);
576
744
  }
577
745
  return this;
578
746
  },
@@ -612,26 +780,25 @@ function Deferred() {
612
780
  isResolved() {
613
781
  return status === RESOLVED;
614
782
  },
615
- resetState() {
616
- status = PENDING;
617
- return this;
783
+ isProgress() {
784
+ return status === PROGRESS;
618
785
  },
619
786
  resolveWith(context, args) {
620
- if (status === PENDING) {
787
+ if (this.isPending()) {
621
788
  status = RESOLVED;
622
789
  executeCallback(context, args, resolveCallbacks);
623
790
  }
624
791
  return this;
625
792
  },
626
793
  rejectWith(context, args) {
627
- if (status === PENDING) {
794
+ if (this.isPending()) {
628
795
  status = REJECTED;
629
796
  executeCallback(context, args, rejectCallbacks);
630
797
  }
631
798
  return this;
632
799
  },
633
800
  notifyWith(context, args) {
634
- if (status === PENDING) {
801
+ if (this.isPending()) {
635
802
  status = PROGRESS;
636
803
  executeCallback(context, args, notifyCallbacks);
637
804
  status = PENDING;
@@ -1338,12 +1505,14 @@ const Api = Module.extend({
1338
1505
  defaults: {
1339
1506
  origin: ApiOrigin,
1340
1507
  endpoint: ApiEndpoint,
1508
+ container: 'body',
1341
1509
  messages: {
1342
1510
  modalHeader: 'Now you will be redirected to your bank 3DSecure. If you are not redirected please refer',
1343
1511
  modalLinkLabel: 'link'
1344
1512
  }
1345
1513
  },
1346
1514
  init(params) {
1515
+ this.iframeDeferred = Deferred();
1347
1516
  this.initParams(params);
1348
1517
  },
1349
1518
  url(type, url) {
@@ -1357,8 +1526,6 @@ const Api = Module.extend({
1357
1526
  this.params = this.utils.extend({}, this.defaults);
1358
1527
  this.extendParams(params);
1359
1528
  this.setOrigin(this.params.origin);
1360
- this.loaded = false;
1361
- this.created = false;
1362
1529
  },
1363
1530
  setOrigin(origin) {
1364
1531
  if (this.utils.isString(origin)) {
@@ -1368,10 +1535,21 @@ const Api = Module.extend({
1368
1535
  },
1369
1536
  scope(callback) {
1370
1537
  callback = this.proxy(callback);
1371
- if (this.createFrame().loaded === true) {
1372
- callback();
1538
+ this.domReady(function () {
1539
+ this.createFrame();
1540
+ if (this.iframeDeferred.isPending()) {
1541
+ this.iframeDeferred.done(callback);
1542
+ } else {
1543
+ callback();
1544
+ }
1545
+ });
1546
+ },
1547
+ domReady(callback) {
1548
+ callback = this.proxy(callback);
1549
+ if (document.readyState === 'loading') {
1550
+ window.addEventListener('DOMContentLoaded', callback);
1373
1551
  } else {
1374
- this.on('checkout.api', callback);
1552
+ callback();
1375
1553
  }
1376
1554
  },
1377
1555
  request(model, method, params) {
@@ -1398,7 +1576,8 @@ const Api = Module.extend({
1398
1576
  }));
1399
1577
  return defer;
1400
1578
  },
1401
- loadFrame(url) {
1579
+ createFrame() {
1580
+ if (this.iframe) return this;
1402
1581
  this.iframe = this.utils.createElement('iframe');
1403
1582
  this.addAttr(this.iframe, {
1404
1583
  allowtransparency: true,
@@ -1406,27 +1585,29 @@ const Api = Module.extend({
1406
1585
  scrolling: 'no'
1407
1586
  });
1408
1587
  this.addAttr(this.iframe, {
1409
- src: url
1588
+ src: this.url('gateway')
1410
1589
  });
1411
1590
  this.addCss(this.iframe, ApiFrameCss);
1412
- this.body = this.utils.querySelector('body');
1413
- if (this.body.firstChild) {
1414
- this.body.insertBefore(this.iframe, this.body.firstChild);
1415
- } else {
1416
- this.body.appendChild(this.iframe);
1591
+ if (this.utils.isElement(this.params.container)) {
1592
+ this.container = this.params.container;
1417
1593
  }
1418
- return this.iframe;
1419
- },
1420
- createFrame() {
1421
- if (this.created === false) {
1422
- this.created = true;
1423
- this.iframe = this.loadFrame(this.url('gateway'));
1594
+ if (this.utils.isString(this.params.container)) {
1595
+ this.container = this.utils.querySelector(this.params.container);
1596
+ }
1597
+ if (this.container) {
1598
+ if (this.container.firstChild) {
1599
+ this.container.insertBefore(this.iframe, this.container.firstChild);
1600
+ } else {
1601
+ this.container.appendChild(this.iframe);
1602
+ }
1424
1603
  this.connector = new Connector({
1425
1604
  target: this.iframe.contentWindow,
1426
1605
  origin: this.params.origin
1427
1606
  });
1428
1607
  this.connector.on('load', this.proxy('onLoadConnector'));
1429
1608
  this.connector.on('modal', this.proxy('onOpenModal'));
1609
+ } else {
1610
+ throw Error(`container element not found: document.querySelector("${this.params.container}")`);
1430
1611
  }
1431
1612
  return this;
1432
1613
  },
@@ -1441,125 +1622,36 @@ const Api = Module.extend({
1441
1622
  this.trigger('modal.close', modal, data);
1442
1623
  },
1443
1624
  onLoadConnector() {
1444
- this.loaded = true;
1625
+ this.iframeDeferred.resolve(true);
1445
1626
  this.connector.off('load');
1446
- this.trigger('checkout.api');
1447
- this.off('checkout.api');
1448
1627
  }
1449
1628
  });
1450
1629
 
1451
- const GooglePayClient = Module.extend({
1452
- id: 'google-payments-api',
1453
- init() {
1454
- this.client = null;
1455
- this.wrapper = this.utils.querySelector('head');
1456
- this.defer = Deferred();
1457
- },
1458
- load() {
1459
- if (this.utils.getPath('google.payments.api.PaymentsClient')) {
1460
- return this.defer.resolveWith(this);
1461
- }
1462
- if (this.utils.querySelector('#'.concat(this.id))) {
1463
- return this.defer;
1464
- }
1465
- this.script = this.utils.createElement('script');
1466
- this.addAttr(this.script, {
1467
- id: this.id,
1468
- async: true,
1469
- src: GooglePayApi
1470
- });
1471
- this.utils.isElement(this.wrapper) && this.wrapper.appendChild(this.script);
1472
- this.addEvent(this.script, 'load', 'onLoadSuccess');
1473
- this.addEvent(this.script, 'error', 'onLoadError');
1474
- return this.defer;
1475
- },
1476
- show(methods) {
1477
- const method = methods.find(function (item) {
1478
- return item.supportedMethods === 'https://google.com/pay';
1479
- });
1480
- const client = this.getClient({
1481
- environment: method.data.environment
1482
- });
1483
- return client.loadPaymentData(method.data);
1484
- },
1485
- readyToPay(cx, response) {
1486
- if (response.result) {
1487
- this.defer.resolveWith(this);
1488
- }
1489
- },
1490
- onError(cx, error) {
1491
- this.defer.rejectWith(this, error);
1492
- },
1493
- onLoadSuccess() {
1494
- const client = this.getClient();
1495
- if (client) client.isReadyToPay(GoogleBaseRequest).then(this.proxy('readyToPay')).catch(this.proxy('onError'));
1496
- },
1497
- onLoadError() {
1498
- this.defer.rejectWith(this);
1499
- },
1500
- getClient(options) {
1501
- if (options || this.client === null) {
1502
- const PaymentClient = this.utils.getPath('google.payments.api.PaymentsClient');
1503
- if (PaymentClient) {
1504
- this.client = new PaymentClient(options);
1505
- } else {
1506
- this.onError(null, new Error('Google Client Error'));
1507
- }
1508
- }
1509
- return this.client;
1510
- }
1511
- });
1512
- const GooglePay = new GooglePayClient();
1513
-
1514
- const getPaymentMethods = () => {
1515
- return [['google', {
1516
- supportedMethods: 'https://google.com/pay',
1517
- data: GoogleBaseRequest
1518
- }], ['apple', {
1519
- supportedMethods: 'https://apple.com/apple-pay'
1520
- }, () => hasProp(window, 'ApplePaySession')]];
1521
- };
1522
- let requestDeferred = null;
1523
- let requestSupported = {
1524
- fallback: false,
1525
- provider: []
1526
- };
1630
+ let supportedMethodsDeferred = null;
1527
1631
  const getSupportedMethods = () => {
1528
- if (requestDeferred) return requestDeferred;
1529
- requestDeferred = Deferred();
1530
- const methods = getPaymentMethods();
1531
- const details = PaymentRequestDetails;
1532
- (function check() {
1533
- const item = methods.shift();
1534
- if (item === undefined) {
1535
- if (requestSupported.provider.indexOf('google') === -1) {
1536
- requestSupported.fallback = true;
1537
- requestSupported.provider.push('google');
1632
+ if (supportedMethodsDeferred) return supportedMethodsDeferred;
1633
+ supportedMethodsDeferred = Deferred();
1634
+ const promises = [];
1635
+ const supported = {
1636
+ provider: []
1637
+ };
1638
+ forEach(PaymentMethodsConfig, method => {
1639
+ promises.push(loadExternalApi(method.url, method.path).then(method.callback).then(state => {
1640
+ if (state) {
1641
+ supported.provider.push(method.name);
1642
+ } else {
1643
+ console.log('error loading api', google);
1538
1644
  }
1539
- return requestDeferred.resolve(requestSupported);
1540
- }
1541
- const method = item.shift();
1542
- const config = item.shift();
1543
- const callback = item.shift();
1544
- if (isFunction(callback) && callback() === false) {
1545
- setTimeout(check, 25);
1546
- return false;
1547
- }
1548
- const request = getPaymentRequest([config], details, {});
1549
- if (request) {
1550
- request.canMakePayment().then(function (status) {
1551
- if (status === true) requestSupported.provider.push(method);
1552
- setTimeout(check, 25);
1553
- }).catch(function () {
1554
- setTimeout(check, 25);
1555
- });
1556
- } else {
1557
- setTimeout(check, 25);
1558
- }
1559
- })();
1560
- return requestDeferred;
1645
+ }));
1646
+ });
1647
+ Promise.all(promises).then(() => {
1648
+ supportedMethodsDeferred.resolve(supported);
1649
+ }, () => {
1650
+ supportedMethodsDeferred.resolve(supported);
1651
+ });
1652
+ return supportedMethodsDeferred;
1561
1653
  };
1562
- const PaymentRequestInterface = Module.extend({
1654
+ const PaymentRequestApi = Module.extend({
1563
1655
  config: {
1564
1656
  payment_system: '',
1565
1657
  fallback: false,
@@ -1598,6 +1690,16 @@ const PaymentRequestInterface = Module.extend({
1598
1690
  setMerchant(merchant) {
1599
1691
  this.merchant = merchant;
1600
1692
  },
1693
+ setBeforeCallback(beforeCallback) {
1694
+ if (isFunction(beforeCallback)) {
1695
+ this.params.before = beforeCallback;
1696
+ }
1697
+ },
1698
+ setAfterCallback(afterCallback) {
1699
+ if (isFunction(afterCallback)) {
1700
+ this.params.after = afterCallback;
1701
+ }
1702
+ },
1601
1703
  setApi(api) {
1602
1704
  if (api instanceof Api) this.api = api;
1603
1705
  return this;
@@ -1610,32 +1712,40 @@ const PaymentRequestInterface = Module.extend({
1610
1712
  },
1611
1713
  isFallbackMethod(method) {
1612
1714
  return method === 'google' && this.supported.fallback;
1613
- }
1614
- });
1615
- const PaymentRequestApi = PaymentRequestInterface.extend({
1616
- request(method, params, success, failure) {
1617
- if (this.api) {
1618
- this.api.scope(this.proxy(function () {
1619
- this.api.request('api.checkout.pay', method, params).done(this.proxy(success)).fail(this.proxy(failure));
1620
- }));
1715
+ },
1716
+ request(model, method, params, success, failure) {
1717
+ const context = this;
1718
+ if (context.api) {
1719
+ context.api.scope(function () {
1720
+ context.api.request(model, method, params).done(success).fail(failure);
1721
+ });
1621
1722
  }
1622
1723
  },
1623
- update(data) {
1724
+ session(params) {
1624
1725
  const defer = Deferred();
1625
- this.request('methods', data, function (cx, model) {
1626
- this.onUpdate(cx, model);
1627
- defer.resolveWith(this, [model]);
1628
- }, function (cx, model) {
1629
- this.onError(cx, model);
1630
- defer.rejectWith(this, [model]);
1726
+ this.request('api.checkout.pay', 'session', params, function (model) {
1727
+ defer.resolve(model.serialize());
1728
+ }, function (model) {
1729
+ defer.reject(model);
1631
1730
  });
1632
1731
  return defer;
1633
1732
  },
1634
- onUpdate(cx, model) {
1635
- this.setPayload(model.serialize());
1636
- },
1637
- onError(cx, model) {
1638
- this.trigger('error', model);
1733
+ update(data) {
1734
+ if (this.isPending()) return;
1735
+ this.setPending(true);
1736
+ const context = this;
1737
+ const defer = Deferred();
1738
+ this.params.data = data;
1739
+ this.request('api.checkout.pay', 'methods', this.params.data, function (model) {
1740
+ context.setPending(false);
1741
+ context.setPayload(model.serialize());
1742
+ defer.resolve(model);
1743
+ }, function (model) {
1744
+ context.setPending(false);
1745
+ context.trigger('error', model);
1746
+ defer.reject(model);
1747
+ });
1748
+ return defer;
1639
1749
  },
1640
1750
  isPending() {
1641
1751
  return this.pendingState === true;
@@ -1643,123 +1753,100 @@ const PaymentRequestApi = PaymentRequestInterface.extend({
1643
1753
  setPending(state) {
1644
1754
  this.pendingState = state;
1645
1755
  clearTimeout(this.pendingTimeoutEvent);
1646
- this.pendingTimeoutEvent = setTimeout(() => {
1647
- this.trigger('pending', state);
1648
- }, 100);
1649
- },
1650
- beforeCallback(defer) {
1651
- defer.resolve();
1652
- },
1653
- setBeforeCallback(callback) {
1654
- if (isFunction(callback)) {
1655
- this.beforeCallback = callback;
1656
- }
1657
- return this;
1756
+ this.pendingTimeoutEvent = setTimeout(function (context) {
1757
+ context.trigger('pending', state);
1758
+ }, 50, this);
1658
1759
  },
1659
1760
  before() {
1660
1761
  if (this.isPending()) return;
1661
1762
  this.setPending(true);
1662
1763
  const defer = Deferred();
1663
- defer.always(this.proxy(function () {
1664
- this.setPending(false);
1665
- }));
1666
- this.beforeCallback(defer);
1764
+ const context = this;
1765
+ defer.always(function () {
1766
+ context.setPending(false);
1767
+ });
1768
+ this.params.before(defer);
1667
1769
  return defer;
1668
1770
  },
1669
- pay(method) {
1670
- if (this.isPending()) return;
1671
- this.setPending(true);
1672
- const payload = this.getProviderPayload(method);
1673
- const response = Deferred();
1674
- response.always(function () {
1675
- this.setPending(false);
1676
- });
1677
- if (this.isMethodSupported(method) === false) {
1678
- return response.rejectWith(this, [{
1679
- test: true
1680
- }]);
1681
- }
1682
- if (this.isFallbackMethod(method)) {
1683
- this.makePaymentFallback(response, payload.methods);
1684
- } else {
1685
- this.makeNativePayment(response, payload);
1686
- }
1687
- return response.done(function (details) {
1688
- this.trigger('details', {
1689
- payment_system: this.payload.payment_system,
1690
- data: details
1691
- });
1692
- }).fail(function (error) {
1693
- this.trigger('error', error);
1694
- if (this.params.embedded === true) {
1695
- location.reload();
1696
- this.trigger('reload', this.params);
1697
- }
1698
- });
1771
+ after(params) {
1772
+ const defer = Deferred();
1773
+ this.params.after(defer, params);
1774
+ return defer;
1699
1775
  },
1700
- makeNativePayment(defer, payload) {
1701
- const self = this;
1702
- const request = getPaymentRequest(payload.methods, payload.details, payload.options);
1703
- this.addEvent(request, 'merchantvalidation', 'merchantValidation');
1704
- request.canMakePayment().then(function () {
1705
- request.show().then(function (response) {
1706
- response.complete('success').then(function () {
1707
- defer.resolveWith(self, [response.details]);
1708
- });
1709
- }).catch(function (e) {
1710
- defer.rejectWith(self, [{
1711
- code: e.code,
1712
- message: e.message
1713
- }]);
1776
+ withApplePay(response, config) {
1777
+ const sessionConfig = getAppleSessionConfig(config);
1778
+ const request = new ApplePaySession(sessionConfig.version, sessionConfig);
1779
+ const merchant = this.merchant;
1780
+ const context = this;
1781
+ request.onvalidatemerchant = function (event) {
1782
+ context.session({
1783
+ url: event.validationURL,
1784
+ domain: location.host,
1785
+ merchant_id: merchant
1786
+ }).done(session => {
1787
+ try {
1788
+ request.completeMerchantValidation(session.data);
1789
+ } catch (error) {
1790
+ response.reject({
1791
+ code: error.code,
1792
+ message: error.message
1793
+ });
1794
+ }
1714
1795
  });
1715
- }).catch(function (e) {
1716
- defer.rejectWith(self, [{
1717
- code: e.code,
1718
- message: e.message
1719
- }]);
1720
- });
1796
+ };
1797
+ request.oncancel = function () {
1798
+ response.reject({
1799
+ code: 20,
1800
+ message: 'cancel'
1801
+ });
1802
+ };
1803
+ request.onpaymentauthorized = function (event) {
1804
+ request.completePayment(ApplePaySession.STATUS_SUCCESS);
1805
+ response.resolve(event.payment);
1806
+ };
1807
+ request.begin();
1721
1808
  },
1722
- makePaymentFallback(defer, methods) {
1723
- const self = this;
1724
- GooglePay.load().then(() => {
1725
- GooglePay.show(methods).then(details => {
1726
- defer.resolveWith(self, [details]);
1727
- }).catch(e => {
1728
- defer.rejectWith(self, [{
1729
- code: e.code,
1730
- message: e.message
1731
- }]);
1809
+ withGooglePay(response, config) {
1810
+ const {
1811
+ methods
1812
+ } = config;
1813
+ const method = methods.find(item => item.supportedMethods === 'https://google.com/pay');
1814
+ const client = new google.payments.api.PaymentsClient({
1815
+ environment: method.data.environment
1816
+ });
1817
+ client.loadPaymentData(method.data).then(paymentData => {
1818
+ response.resolve(paymentData);
1819
+ }).catch(error => {
1820
+ response.reject({
1821
+ code: error.code,
1822
+ message: error.message
1732
1823
  });
1733
1824
  });
1734
1825
  },
1735
- appleSession(params) {
1736
- const defer = Deferred();
1737
- this.request('session', params, function (c, model) {
1738
- defer.resolveWith(this, [model.serialize()]);
1739
- }, function (c, model) {
1740
- defer.rejectWith(this, [model]);
1826
+ pay(method) {
1827
+ if (this.isPending()) return;
1828
+ this.setPending(true);
1829
+ const context = this;
1830
+ const response = Deferred();
1831
+ const payload = this.getProviderPayload(method);
1832
+ response.always(function () {
1833
+ context.setPending(false);
1741
1834
  });
1742
- return defer;
1743
- },
1744
- merchantValidation(cx, event) {
1745
- const {
1746
- validationURL
1747
- } = event;
1748
- const {
1749
- host
1750
- } = location;
1751
- this.appleSession({
1752
- url: validationURL,
1753
- domain: host,
1754
- merchant_id: this.merchant
1755
- }).done(function (session) {
1756
- try {
1757
- event.complete(session.data);
1758
- } catch (error) {
1759
- this.trigger('error', error);
1760
- }
1835
+ switch (method) {
1836
+ case 'google':
1837
+ this.withGooglePay(response, payload);
1838
+ break;
1839
+ case 'apple':
1840
+ this.withApplePay(response, payload);
1841
+ break;
1842
+ }
1843
+ return response.done(function (data) {
1844
+ context.trigger('details', {
1845
+ payment_system: context.payload.payment_system,
1846
+ data
1847
+ });
1761
1848
  }).fail(function (error) {
1762
- this.trigger('error', error);
1849
+ context.trigger('error', error);
1763
1850
  });
1764
1851
  }
1765
1852
  });
@@ -1777,11 +1864,22 @@ const PaymentElement = Module.extend({
1777
1864
  height: 38
1778
1865
  },
1779
1866
  init(params) {
1867
+ this.state = {
1868
+ isSupported: false,
1869
+ isAllowed: false,
1870
+ isMounted: false
1871
+ };
1780
1872
  this.params = {};
1781
1873
  this.utils.extend(this.params, this.defaults);
1782
1874
  this.utils.extend(this.params, params);
1783
1875
  this.initElement();
1784
1876
  },
1877
+ timeout(callback, timeout) {
1878
+ this.timeoutMap = this.timeoutMap || {};
1879
+ clearTimeout(this.timeoutMap[timeout]);
1880
+ this.timeoutMap[callback] = setTimeout(callback, timeout);
1881
+ return this;
1882
+ },
1785
1883
  getElementUrl() {
1786
1884
  return [this.params.origin, this.params.endpoint].join('');
1787
1885
  },
@@ -1842,19 +1940,18 @@ const PaymentElement = Module.extend({
1842
1940
  },
1843
1941
  onClickFail() {},
1844
1942
  onSupported(cx, supported) {
1845
- if (supported.provider.includes(this.params.method)) {
1846
- this.mount();
1847
- }
1943
+ this.state.isSupported = supported.provider.includes(this.params.method);
1848
1944
  },
1849
1945
  onPayload(cx, payload) {
1850
- if (payload.allowed.includes(this.params.method)) {
1851
- this.show();
1852
- }
1946
+ this.state.isAllowed = payload.allowed.includes(this.params.method);
1947
+ this.render();
1853
1948
  },
1854
1949
  onPending(cx, state) {
1855
1950
  this.pending = state;
1856
- this.send('pending', {
1857
- state: state
1951
+ this.addCss(this.element, {
1952
+ transition: 'height 0.2s ease-out, opacity 0.4s ease-out',
1953
+ pointerEvents: this.pending ? 'none' : '',
1954
+ opacity: this.pending ? '0.5' : '1'
1858
1955
  });
1859
1956
  },
1860
1957
  initEvents() {
@@ -1881,36 +1978,50 @@ const PaymentElement = Module.extend({
1881
1978
  this.initEvents();
1882
1979
  return this;
1883
1980
  },
1884
- mount() {
1885
- this.appendTo(this.params.appendTo);
1981
+ render() {
1982
+ if (this.state.isSupported === false) return this;
1983
+ if (this.state.isAllowed === false) return this;
1984
+ this.mount();
1985
+ this.show();
1886
1986
  },
1887
- isMounted() {
1888
- return document.body.contains(this.element);
1987
+ unmount() {
1988
+ if (this.element.parentNode) {
1989
+ this.element.parentNode.removeChild(this.element);
1990
+ }
1991
+ return this;
1992
+ },
1993
+ mount() {
1994
+ if (document.body.contains(this.element) === false) {
1995
+ this.appendTo(this.params.appendTo);
1996
+ }
1997
+ return this;
1889
1998
  },
1890
1999
  show() {
1891
- if (this.isMounted() === false) return this;
1892
- this.addCss(this.iframe, {
1893
- transition: 'opacity 0.6s 0.4s ease-out',
1894
- opacity: this.utils.cssUnit(1)
1895
- });
1896
- this.addCss(this.element, {
1897
- transition: 'height 0.2s ease-out',
1898
- height: this.utils.cssUnit(this.params.height, 'px')
1899
- });
1900
- this.trigger('show', {});
2000
+ this.timeout(() => {
2001
+ this.addCss(this.iframe, {
2002
+ transition: 'opacity 0.6s 0.4s ease-out',
2003
+ opacity: this.utils.cssUnit(1)
2004
+ });
2005
+ this.addCss(this.element, {
2006
+ transition: 'height 0.2s ease-out',
2007
+ height: this.utils.cssUnit(this.params.height, 'px')
2008
+ });
2009
+ this.trigger('show', {});
2010
+ }, 25);
1901
2011
  return this;
1902
2012
  },
1903
2013
  hide() {
1904
- if (this.isMounted() === false) return this;
1905
- this.addCss(this.iframe, {
1906
- transition: 'opacity 0.4s ease-out',
1907
- opacity: this.utils.cssUnit(0)
1908
- });
1909
- this.addCss(this.element, {
1910
- transition: 'height 0.2s 0.4s ease-out',
1911
- height: this.utils.cssUnit(0, 'px')
1912
- });
1913
- this.trigger('hide', {});
2014
+ this.timeout(() => {
2015
+ this.addCss(this.iframe, {
2016
+ transition: 'opacity 0.4s ease-out',
2017
+ opacity: this.utils.cssUnit(0)
2018
+ });
2019
+ this.addCss(this.element, {
2020
+ transition: 'height 0.2s 0.4s ease-out',
2021
+ height: this.utils.cssUnit(0, 'px')
2022
+ });
2023
+ this.trigger('hide', {});
2024
+ }, 25);
1914
2025
  return this;
1915
2026
  }
1916
2027
  });
@@ -1932,6 +2043,9 @@ const PaymentButton = Module.extend({
1932
2043
  },
1933
2044
  before(defer) {
1934
2045
  defer.resolve();
2046
+ },
2047
+ after(defer, params) {
2048
+ defer.resolve(params);
1935
2049
  }
1936
2050
  },
1937
2051
  init(params) {
@@ -1943,7 +2057,8 @@ const PaymentButton = Module.extend({
1943
2057
  origin: params.origin || this.defaults.origin,
1944
2058
  endpoint: this.utils.extend({}, this.defaults.endpoint, params.endpoint),
1945
2059
  style: this.utils.extend({}, this.defaults.style, params.style),
1946
- before: params.before || this.defaults.before,
2060
+ before: isFunction(params.before) ? params.before : this.defaults.before,
2061
+ after: isFunction(params.after) ? params.after : this.defaults.after,
1947
2062
  data: this.utils.extend({}, this.defaults.data, params.data)
1948
2063
  };
1949
2064
  this.initApi(params.api);
@@ -1968,10 +2083,12 @@ const PaymentButton = Module.extend({
1968
2083
  }
1969
2084
  },
1970
2085
  initPaymentRequestApi() {
1971
- this.request = new PaymentRequestApi();
2086
+ this.request = new PaymentRequestApi({
2087
+ before: this.params.before,
2088
+ after: this.params.after
2089
+ });
1972
2090
  this.request.setApi(this.api);
1973
2091
  this.request.setMerchant(this.params.data.merchant_id);
1974
- this.request.setBeforeCallback(this.params.before);
1975
2092
  this.request.on('details', this.proxy('onDetails'));
1976
2093
  this.request.on('error', this.proxy('onError'));
1977
2094
  },
@@ -2008,7 +2125,9 @@ const PaymentButton = Module.extend({
2008
2125
  },
2009
2126
  onDetails(cx, data) {
2010
2127
  this.api.scope(() => {
2011
- this.api.request('api.checkout.form', 'request', this.utils.extend({}, this.params.data, data)).done(this.proxy('onSuccess')).fail(this.proxy('onError'));
2128
+ this.request.after(this.params.data).done(extendParams => {
2129
+ this.api.request('api.checkout.form', 'request', this.utils.extend({}, this.params.data, extendParams || {}, data)).done(this.proxy('onSuccess')).fail(this.proxy('onError'));
2130
+ });
2012
2131
  });
2013
2132
  },
2014
2133
  onSuccess(cx, data) {