@flittpayments/js-sdk 2.0.5 → 2.0.7

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.
@@ -354,8 +354,134 @@ const jsonParse = (value, defaults) => {
354
354
  }
355
355
  };
356
356
 
357
+ const PromiseWithResolvers = () => {
358
+ let resolve;
359
+ let reject;
360
+ const promise = new Promise((promiseResolve, promiseReject) => {
361
+ resolve = promiseResolve;
362
+ reject = promiseReject;
363
+ });
364
+ return { promise, resolve, reject }
365
+ };
366
+
367
+ /**
368
+ * @typedef PaymentRequestMethod
369
+ * @property {{[string]:any}} data
370
+ */
371
+
372
+ /**
373
+ * @typedef PaymentRequestOptions
374
+ * @property {boolean} [requestPayerEmail]
375
+ * @property {boolean} [requestPayerPhone]
376
+ * @property {boolean} [requestPayerName]
377
+ * @property {boolean} [requestShipping]
378
+ */
379
+
380
+ /**
381
+ * @typedef PaymentRequestConfig
382
+ * @property [PaymentRequestMethod] methods
383
+ * @property {{[string]:any}} options
384
+ */
385
+
386
+ /**
387
+ * @param {PaymentRequestConfig} config
388
+ */
389
+ const getAppleSessionConfig = (config) => {
390
+ const { methods, details, options } = config;
391
+ const method = methods.find((item) => item.supportedMethods === 'https://apple.com/apple-pay');
392
+ const requiredShippingContactFields = [];
393
+ const requiredBillingContactFields = [];
394
+ if (options.requestShipping) requiredBillingContactFields.push('postalAddress');
395
+ if (options.requestPayerName) requiredShippingContactFields.push('name');
396
+ if (options.requestPayerEmail) requiredShippingContactFields.push('email');
397
+ if (options.requestPayerPhone) requiredShippingContactFields.push('phone');
398
+ //method.data.recurringPaymentRequest = recurringPaymentRequest
399
+ //method.data.deferredPaymentRequest = deferredPaymentRequest
400
+ return {
401
+ version: method.data.version,
402
+ countryCode: method.data.countryCode,
403
+ supportedNetworks: method.data.supportedNetworks,
404
+ merchantCapabilities: method.data.merchantCapabilities,
405
+ merchantIdentifier: method.data.merchantIdentifier,
406
+ lineItems: method.data.lineItems || details.displayItems,
407
+ additionalLineItems: method.data.additionalLineItems,
408
+ recurringPaymentRequest: method.data.recurringPaymentRequest,
409
+ deferredPaymentRequest: method.data.deferredPaymentRequest,
410
+ //
411
+ currencyCode: method.data.currencyCode || details.total.amount.currency,
412
+ total: {
413
+ label: details.total.label,
414
+ amount: details.total.amount.value,
415
+ },
416
+ //
417
+ requiredBillingContactFields,
418
+ requiredShippingContactFields,
419
+ }
420
+ };
421
+
422
+ // export const deferredPaymentRequest = {
423
+ // paymentDescription: 'Бронювання готельного номеру',
424
+ // deferredBilling: {
425
+ // label: 'Бронювання',
426
+ // amount: '50.00',
427
+ // type: 'final',
428
+ // paymentTiming: 'deferred',
429
+ // deferredPaymentDate: new Date('2026-01-05'),
430
+ // },
431
+ // managementURL: 'https://flitt.ksv.app/manage-booking.html',
432
+ // }
433
+
434
+ // export const recurringPaymentRequest = {
435
+ // paymentDescription: 'Місячна підписка',
436
+ // regularBilling: {
437
+ // label: 'Підписка на місяць',
438
+ // amount: '20.00',
439
+ // type: 'final',
440
+ // paymentTiming: 'recurring',
441
+ // recurringPaymentStartDate: new Date('2026-01-01T00:00:00'),
442
+ // recurringPaymentIntervalUnit: 'month',
443
+ // recurringPaymentIntervalCount: 6,
444
+ // recurringPaymentEndDate: new Date('2028-01-01T00:00:00'),
445
+ // },
446
+ // managementURL: 'https://flitt.ksv.app/manage-subscription.html',
447
+ // }
448
+
449
+ const getFunction = (path) => {
450
+ const props = path.split('.');
451
+ const value = props.reduce((acc, cur) => {
452
+ if (acc && acc.hasOwnProperty(cur)) {
453
+ acc = acc[cur];
454
+ return acc
455
+ }
456
+ return null
457
+ }, window);
458
+ return isFunction(value) ? value : value
459
+ };
460
+
461
+ const loadExternalApi = (url, path) => {
462
+ const [existScript] = toArray(document.scripts).filter((script) => script.src === url);
463
+ if (existScript) return Promise.resolve(getFunction(path))
464
+ const { promise, resolve } = PromiseWithResolvers();
465
+ const script = document.createElement('script');
466
+ script.async = true;
467
+ script.src = url;
468
+ script.addEventListener('load', () => {
469
+ const value = getFunction(path);
470
+ console.log('api loading', 'success', url);
471
+ resolve(value);
472
+ });
473
+ script.addEventListener('error', (e) => {
474
+ const value = getFunction(path);
475
+ console.log('api loading', 'error', url);
476
+ resolve(value);
477
+ });
478
+ document.head.appendChild(script);
479
+ return promise
480
+ };
481
+
357
482
  var Utils = /*#__PURE__*/Object.freeze({
358
483
  __proto__: null,
484
+ PromiseWithResolvers: PromiseWithResolvers,
359
485
  addAttr: addAttr,
360
486
  addClass: addClass,
361
487
  cleanObject: cleanObject,
@@ -364,6 +490,8 @@ var Utils = /*#__PURE__*/Object.freeze({
364
490
  deparam: deparam,
365
491
  extend: extend,
366
492
  forEach: forEach,
493
+ getAppleSessionConfig: getAppleSessionConfig,
494
+ getFunction: getFunction,
367
495
  getPath: getPath,
368
496
  getPaymentRequest: getPaymentRequest,
369
497
  getStyle: getStyle,
@@ -381,6 +509,7 @@ var Utils = /*#__PURE__*/Object.freeze({
381
509
  isRegexp: isRegexp,
382
510
  isString: isString,
383
511
  jsonParse: jsonParse,
512
+ loadExternalApi: loadExternalApi,
384
513
  map: map,
385
514
  param: param,
386
515
  querySelector: querySelector,
@@ -418,6 +547,36 @@ const GoogleBaseRequest = {
418
547
  ],
419
548
  };
420
549
 
550
+ const PaymentMethodsConfig = [
551
+ {
552
+ url: 'https://pay.google.com/gp/p/js/pay.js',
553
+ name: 'google',
554
+ path: 'google.payments.api.PaymentsClient',
555
+ callback(PaymentsClient) {
556
+ if (PaymentsClient) {
557
+ return new PaymentsClient({ environment: 'TEST' })
558
+ .isReadyToPay(GoogleBaseRequest)
559
+ .catch((e) => {
560
+ console.log(e);
561
+ return false
562
+ })
563
+ }
564
+ return false
565
+ },
566
+ },
567
+ {
568
+ url: 'https://applepay.cdn-apple.com/jsapi/1.latest/apple-pay-sdk.js',
569
+ name: 'apple',
570
+ path: 'ApplePaySession',
571
+ callback(ApplePaySession) {
572
+ if (ApplePaySession) {
573
+ return ApplePaySession.supportsVersion(3)
574
+ }
575
+ return false
576
+ },
577
+ },
578
+ ];
579
+
421
580
  const GooglePayLanguages = [
422
581
  'ar',
423
582
  'bg',
@@ -587,6 +746,7 @@ var Config = /*#__PURE__*/Object.freeze({
587
746
  GoogleBaseRequest: GoogleBaseRequest,
588
747
  GooglePayApi: GooglePayApi,
589
748
  GooglePayLanguages: GooglePayLanguages,
749
+ PaymentMethodsConfig: PaymentMethodsConfig,
590
750
  PaymentRequestDetails: PaymentRequestDetails,
591
751
  ProxyUrl: ProxyUrl
592
752
  });
@@ -642,28 +802,31 @@ function Deferred() {
642
802
  const promise = {
643
803
  done(callback) {
644
804
  if (isFunction(callback)) {
645
- if (status === RESOLVED) {
805
+ if (this.isResolved()) {
646
806
  callback.apply(resultContext, resultArgs);
807
+ } else {
808
+ resolveCallbacks.push(callback);
647
809
  }
648
- resolveCallbacks.push(callback);
649
810
  }
650
811
  return this
651
812
  },
652
813
  fail(callback) {
653
814
  if (isFunction(callback)) {
654
- if (status === REJECTED) {
815
+ if (this.isRejected()) {
655
816
  callback.apply(resultContext, resultArgs);
817
+ } else {
818
+ rejectCallbacks.push(callback);
656
819
  }
657
- rejectCallbacks.push(callback);
658
820
  }
659
821
  return this
660
822
  },
661
823
  progress(callback) {
662
824
  if (isFunction(callback)) {
663
- if (status === PROGRESS) {
825
+ if (this.isProgress()) {
664
826
  callback.apply(resultContext, resultArgs);
827
+ } else {
828
+ notifyCallbacks.push(callback);
665
829
  }
666
- notifyCallbacks.push(callback);
667
830
  }
668
831
  return this
669
832
  },
@@ -703,33 +866,31 @@ function Deferred() {
703
866
  isResolved() {
704
867
  return status === RESOLVED
705
868
  },
706
- resetState() {
707
- status = PENDING;
708
- return this
869
+ isProgress() {
870
+ return status === PROGRESS
709
871
  },
710
872
  resolveWith(context, args) {
711
- if (status === PENDING) {
873
+ if (this.isPending()) {
712
874
  status = RESOLVED;
713
875
  executeCallback(context, args, resolveCallbacks);
714
876
  }
715
877
  return this
716
878
  },
717
879
  rejectWith(context, args) {
718
- if (status === PENDING) {
880
+ if (this.isPending()) {
719
881
  status = REJECTED;
720
882
  executeCallback(context, args, rejectCallbacks);
721
883
  }
722
884
  return this
723
885
  },
724
886
  notifyWith(context, args) {
725
- if (status === PENDING) {
887
+ if (this.isPending()) {
726
888
  status = PROGRESS;
727
889
  executeCallback(context, args, notifyCallbacks);
728
890
  status = PENDING;
729
891
  }
730
892
  return this
731
893
  },
732
-
733
894
  resolve() {
734
895
  return this.resolveWith(this, Array.from(arguments))
735
896
  },
@@ -1474,6 +1635,7 @@ const Api = Module.extend({
1474
1635
  },
1475
1636
  },
1476
1637
  init(params) {
1638
+ this.iframeDeferred = Deferred();
1477
1639
  this.initParams(params);
1478
1640
  },
1479
1641
  url(type, url) {
@@ -1487,8 +1649,6 @@ const Api = Module.extend({
1487
1649
  this.params = this.utils.extend({}, this.defaults);
1488
1650
  this.extendParams(params);
1489
1651
  this.setOrigin(this.params.origin);
1490
- this.loaded = false;
1491
- this.created = false;
1492
1652
  },
1493
1653
  setOrigin(origin) {
1494
1654
  if (this.utils.isString(origin)) {
@@ -1499,18 +1659,21 @@ const Api = Module.extend({
1499
1659
  scope(callback) {
1500
1660
  callback = this.proxy(callback);
1501
1661
  this.domReady(function () {
1502
- if (this.createFrame().loaded === true) {
1503
- callback();
1662
+ this.createFrame();
1663
+ if (this.iframeDeferred.isPending()) {
1664
+ this.iframeDeferred.done(callback);
1504
1665
  } else {
1505
- this.on('checkout.api', callback);
1666
+ callback();
1506
1667
  }
1507
1668
  });
1508
1669
  },
1509
1670
  domReady(callback) {
1510
1671
  callback = this.proxy(callback);
1511
- document.readyState !== 'loading'
1512
- ? callback()
1513
- : window.addEventListener('DOMContentLoaded', callback);
1672
+ if (document.readyState === 'loading') {
1673
+ window.addEventListener('DOMContentLoaded', callback);
1674
+ } else {
1675
+ callback();
1676
+ }
1514
1677
  },
1515
1678
  request(model, method, params) {
1516
1679
  const defer = Deferred();
@@ -1539,191 +1702,84 @@ const Api = Module.extend({
1539
1702
  );
1540
1703
  return defer
1541
1704
  },
1542
-
1543
- loadFrame(url) {
1705
+ createFrame() {
1706
+ if (this.iframe) return this
1544
1707
  this.iframe = this.utils.createElement('iframe');
1545
1708
  this.addAttr(this.iframe, {
1546
1709
  allowtransparency: true,
1547
1710
  frameborder: 0,
1548
1711
  scrolling: 'no',
1549
1712
  });
1550
- this.addAttr(this.iframe, { src: url });
1713
+ this.addAttr(this.iframe, { src: this.url('gateway') });
1551
1714
  this.addCss(this.iframe, ApiFrameCss);
1552
- this.container = this.utils.querySelector(this.params.container);
1715
+ if (this.utils.isElement(this.params.container)) {
1716
+ this.container = this.params.container;
1717
+ }
1718
+ if (this.utils.isString(this.params.container)) {
1719
+ this.container = this.utils.querySelector(this.params.container);
1720
+ }
1553
1721
  if (this.container) {
1554
1722
  if (this.container.firstChild) {
1555
1723
  this.container.insertBefore(this.iframe, this.container.firstChild);
1556
1724
  } else {
1557
1725
  this.container.appendChild(this.iframe);
1558
1726
  }
1559
- } else {
1560
- throw Error(`container element not found: querySelector("${this.params.container}")`)
1561
- }
1562
- return this.iframe
1563
- },
1564
- createFrame() {
1565
- if (this.created === false) {
1566
- this.created = true;
1567
- this.iframe = this.loadFrame(this.url('gateway'));
1568
1727
  this.connector = new Connector({
1569
1728
  target: this.iframe.contentWindow,
1570
1729
  origin: this.params.origin,
1571
1730
  });
1572
1731
  this.connector.on('load', this.proxy('onLoadConnector'));
1573
1732
  this.connector.on('modal', this.proxy('onOpenModal'));
1733
+ } else {
1734
+ throw Error(`container element not found: document.querySelector("${this.params.container}")`)
1574
1735
  }
1575
1736
  return this
1576
1737
  },
1577
1738
  onOpenModal(xhr, model) {
1578
- this.modal = new Modal({
1579
- checkout: this,
1580
- model: model,
1581
- });
1739
+ this.modal = new Modal({ checkout: this, model: model });
1582
1740
  this.modal.on('close', this.proxy('onCloseModal'));
1583
1741
  },
1584
1742
  onCloseModal(modal, data) {
1585
1743
  this.trigger('modal.close', modal, data);
1586
1744
  },
1587
1745
  onLoadConnector() {
1588
- this.loaded = true;
1746
+ this.iframeDeferred.resolve(true);
1589
1747
  this.connector.off('load');
1590
- this.trigger('checkout.api');
1591
- this.off('checkout.api');
1592
- },
1593
- });
1594
-
1595
- const GooglePayClient = Module.extend({
1596
- id: 'google-payments-api',
1597
- init() {
1598
- this.client = null;
1599
- this.wrapper = this.utils.querySelector('head');
1600
- this.defer = Deferred();
1601
- },
1602
- load() {
1603
- if (this.utils.getPath('google.payments.api.PaymentsClient')) {
1604
- return this.defer.resolveWith(this)
1605
- }
1606
- if (this.utils.querySelector('#'.concat(this.id))) {
1607
- return this.defer
1608
- }
1609
- this.script = this.utils.createElement('script');
1610
- this.addAttr(this.script, {
1611
- id: this.id,
1612
- async: true,
1613
- src: GooglePayApi,
1614
- });
1615
- this.utils.isElement(this.wrapper) && this.wrapper.appendChild(this.script);
1616
- this.addEvent(this.script, 'load', 'onLoadSuccess');
1617
- this.addEvent(this.script, 'error', 'onLoadError');
1618
- return this.defer
1619
- },
1620
- show(methods) {
1621
- const method = methods.find(function (item) {
1622
- return item.supportedMethods === 'https://google.com/pay'
1623
- });
1624
- const client = this.getClient({ environment: method.data.environment });
1625
- return client.loadPaymentData(method.data)
1626
- },
1627
- readyToPay(cx, response) {
1628
- if (response.result) {
1629
- this.defer.resolveWith(this);
1630
- }
1631
- },
1632
- onError(cx, error) {
1633
- this.defer.rejectWith(this, error);
1634
- },
1635
- onLoadSuccess() {
1636
- const client = this.getClient();
1637
- if (client)
1638
- client
1639
- .isReadyToPay(GoogleBaseRequest)
1640
- .then(this.proxy('readyToPay'))
1641
- .catch(this.proxy('onError'));
1642
- },
1643
- onLoadError() {
1644
- this.defer.rejectWith(this);
1645
- },
1646
- getClient(options) {
1647
- if (options || this.client === null) {
1648
- const PaymentClient = this.utils.getPath('google.payments.api.PaymentsClient');
1649
- if (PaymentClient) {
1650
- this.client = new PaymentClient(options);
1651
- } else {
1652
- this.onError(null, new Error('Google Client Error'));
1653
- }
1654
- }
1655
- return this.client
1656
1748
  },
1657
1749
  });
1658
1750
 
1659
- const GooglePay = new GooglePayClient();
1660
-
1661
- const getPaymentMethods = () => {
1662
- return [
1663
- [
1664
- 'google',
1665
- {
1666
- supportedMethods: 'https://google.com/pay',
1667
- data: GoogleBaseRequest,
1668
- },
1669
- ],
1670
- [
1671
- 'apple',
1672
- {
1673
- supportedMethods: 'https://apple.com/apple-pay',
1674
- },
1675
- () => hasProp(window, 'ApplePaySession'),
1676
- ],
1677
- ]
1678
- };
1679
-
1680
- let requestDeferred = null;
1681
-
1682
- let requestSupported = {
1683
- fallback: false,
1684
- provider: [],
1685
- };
1751
+ let supportedMethodsDeferred = null;
1686
1752
 
1687
1753
  const getSupportedMethods = () => {
1688
- if (requestDeferred) return requestDeferred
1689
- requestDeferred = Deferred();
1690
- const methods = getPaymentMethods();
1691
- const details = PaymentRequestDetails
1692
- ;(function check() {
1693
- const item = methods.shift();
1694
- if (item === undefined) {
1695
- if (requestSupported.provider.indexOf('google') === -1) {
1696
- requestSupported.fallback = true;
1697
- requestSupported.provider.push('google');
1698
- }
1699
- return requestDeferred.resolve(requestSupported)
1700
- }
1701
- const method = item.shift();
1702
- const config = item.shift();
1703
- const callback = item.shift();
1704
- if (isFunction(callback) && callback() === false) {
1705
- setTimeout(check, 25);
1706
- return false
1707
- }
1708
- const request = getPaymentRequest([config], details, {});
1709
- if (request) {
1710
- request
1711
- .canMakePayment()
1712
- .then(function (status) {
1713
- if (status === true) requestSupported.provider.push(method);
1714
- setTimeout(check, 25);
1754
+ if (supportedMethodsDeferred) return supportedMethodsDeferred
1755
+ supportedMethodsDeferred = Deferred();
1756
+ const promises = [];
1757
+ const supported = { provider: [] };
1758
+ forEach(PaymentMethodsConfig, (method) => {
1759
+ promises.push(
1760
+ loadExternalApi(method.url, method.path)
1761
+ .then(method.callback)
1762
+ .then((state) => {
1763
+ if (state) {
1764
+ supported.provider.push(method.name);
1765
+ } else {
1766
+ console.log('error loading api', google);
1767
+ }
1715
1768
  })
1716
- .catch(function () {
1717
- setTimeout(check, 25);
1718
- });
1719
- } else {
1720
- setTimeout(check, 25);
1769
+ );
1770
+ });
1771
+ Promise.all(promises).then(
1772
+ () => {
1773
+ supportedMethodsDeferred.resolve(supported);
1774
+ },
1775
+ () => {
1776
+ supportedMethodsDeferred.resolve(supported);
1721
1777
  }
1722
- })();
1723
- return requestDeferred
1778
+ );
1779
+ return supportedMethodsDeferred
1724
1780
  };
1725
1781
 
1726
- const PaymentRequestInterface = Module.extend({
1782
+ const PaymentRequestApi = Module.extend({
1727
1783
  config: {
1728
1784
  payment_system: '',
1729
1785
  fallback: false,
@@ -1762,6 +1818,16 @@ const PaymentRequestInterface = Module.extend({
1762
1818
  setMerchant(merchant) {
1763
1819
  this.merchant = merchant;
1764
1820
  },
1821
+ setBeforeCallback(beforeCallback) {
1822
+ if (isFunction(beforeCallback)) {
1823
+ this.params.before = beforeCallback;
1824
+ }
1825
+ },
1826
+ setAfterCallback(afterCallback) {
1827
+ if (isFunction(afterCallback)) {
1828
+ this.params.after = afterCallback;
1829
+ }
1830
+ },
1765
1831
  setApi(api) {
1766
1832
  if (api instanceof Api) this.api = api;
1767
1833
  return this
@@ -1775,172 +1841,152 @@ const PaymentRequestInterface = Module.extend({
1775
1841
  isFallbackMethod(method) {
1776
1842
  return method === 'google' && this.supported.fallback
1777
1843
  },
1778
- });
1779
-
1780
- const PaymentRequestApi = PaymentRequestInterface.extend({
1781
- request(method, params, success, failure) {
1782
- if (this.api) {
1783
- this.api.scope(
1784
- this.proxy(function () {
1785
- this.api
1786
- .request('api.checkout.pay', method, params)
1787
- .done(this.proxy(success))
1788
- .fail(this.proxy(failure));
1789
- })
1790
- );
1844
+ request(model, method, params, success, failure) {
1845
+ const context = this;
1846
+ if (context.api) {
1847
+ context.api.scope(function () {
1848
+ context.api.request(model, method, params).done(success).fail(failure);
1849
+ });
1791
1850
  }
1792
1851
  },
1852
+ session(params) {
1853
+ const defer = Deferred();
1854
+ this.request(
1855
+ 'api.checkout.pay',
1856
+ 'session',
1857
+ params,
1858
+ function (model) {
1859
+ defer.resolve(model.serialize());
1860
+ },
1861
+ function (model) {
1862
+ defer.reject(model);
1863
+ }
1864
+ );
1865
+ return defer
1866
+ },
1793
1867
  update(data) {
1868
+ if (this.isPending()) return
1869
+ this.setPending(true);
1870
+ const context = this;
1794
1871
  const defer = Deferred();
1872
+ this.params.data = data;
1795
1873
  this.request(
1874
+ 'api.checkout.pay',
1796
1875
  'methods',
1797
- data,
1798
- function (cx, model) {
1799
- this.onUpdate(cx, model);
1800
- defer.resolveWith(this, [model]);
1876
+ this.params.data,
1877
+ function (model) {
1878
+ context.setPending(false);
1879
+ context.setPayload(model.serialize());
1880
+ defer.resolve(model);
1801
1881
  },
1802
- function (cx, model) {
1803
- this.onError(cx, model);
1804
- defer.rejectWith(this, [model]);
1882
+ function (model) {
1883
+ context.setPending(false);
1884
+ context.trigger('error', model);
1885
+ defer.reject(model);
1805
1886
  }
1806
1887
  );
1807
1888
  return defer
1808
1889
  },
1809
- onUpdate(cx, model) {
1810
- this.setPayload(model.serialize());
1811
- },
1812
- onError(cx, model) {
1813
- this.trigger('error', model);
1814
- },
1815
1890
  isPending() {
1816
1891
  return this.pendingState === true
1817
1892
  },
1818
1893
  setPending(state) {
1819
1894
  this.pendingState = state;
1820
1895
  clearTimeout(this.pendingTimeoutEvent);
1821
- this.pendingTimeoutEvent = setTimeout(() => {
1822
- this.trigger('pending', state);
1823
- }, 100);
1824
- },
1825
- beforeCallback(defer) {
1826
- defer.resolve();
1827
- },
1828
- setBeforeCallback(callback) {
1829
- if (isFunction(callback)) {
1830
- this.beforeCallback = callback;
1831
- }
1832
- return this
1896
+ this.pendingTimeoutEvent = setTimeout(
1897
+ function (context) {
1898
+ context.trigger('pending', state);
1899
+ },
1900
+ 50,
1901
+ this
1902
+ );
1833
1903
  },
1834
1904
  before() {
1835
1905
  if (this.isPending()) return
1836
1906
  this.setPending(true);
1837
1907
  const defer = Deferred();
1838
- defer.always(
1839
- this.proxy(function () {
1840
- this.setPending(false);
1841
- })
1842
- );
1843
- this.beforeCallback(defer);
1908
+ const context = this;
1909
+ defer.always(function () {
1910
+ context.setPending(false);
1911
+ });
1912
+ this.params.before(defer);
1913
+ return defer
1914
+ },
1915
+ after(params) {
1916
+ const defer = Deferred();
1917
+ this.params.after(defer, params);
1844
1918
  return defer
1845
1919
  },
1920
+ withApplePay(response, config) {
1921
+ const sessionConfig = getAppleSessionConfig(config);
1922
+ const request = new ApplePaySession(sessionConfig.version, sessionConfig);
1923
+ const merchant = this.merchant;
1924
+ const context = this;
1925
+ request.onvalidatemerchant = function (event) {
1926
+ context
1927
+ .session({
1928
+ url: event.validationURL,
1929
+ domain: location.host,
1930
+ merchant_id: merchant,
1931
+ })
1932
+ .done((session) => {
1933
+ try {
1934
+ request.completeMerchantValidation(session.data);
1935
+ } catch (error) {
1936
+ response.reject({ code: error.code, message: error.message });
1937
+ }
1938
+ });
1939
+ };
1940
+ request.oncancel = function () {
1941
+ response.reject({ code: 20, message: 'cancel' });
1942
+ };
1943
+ request.onpaymentauthorized = function (event) {
1944
+ request.completePayment(ApplePaySession.STATUS_SUCCESS);
1945
+ response.resolve(event.payment);
1946
+ };
1947
+ request.begin();
1948
+ },
1949
+ withGooglePay(response, config) {
1950
+ const { methods } = config;
1951
+ const method = methods.find((item) => item.supportedMethods === 'https://google.com/pay');
1952
+ const client = new google.payments.api.PaymentsClient({ environment: method.data.environment });
1953
+ client
1954
+ .loadPaymentData(method.data)
1955
+ .then((paymentData) => {
1956
+ response.resolve(paymentData);
1957
+ })
1958
+ .catch((error) => {
1959
+ response.reject({ code: error.code, message: error.message });
1960
+ });
1961
+ },
1846
1962
  pay(method) {
1847
1963
  if (this.isPending()) return
1848
1964
  this.setPending(true);
1849
- const payload = this.getProviderPayload(method);
1965
+ const context = this;
1850
1966
  const response = Deferred();
1967
+ const payload = this.getProviderPayload(method);
1851
1968
  response.always(function () {
1852
- this.setPending(false);
1969
+ context.setPending(false);
1853
1970
  });
1854
- if (this.isMethodSupported(method) === false) {
1855
- return response.rejectWith(this, [{ test: true }])
1856
- }
1857
- if (this.isFallbackMethod(method)) {
1858
- this.makePaymentFallback(response, payload.methods);
1859
- } else {
1860
- this.makeNativePayment(response, payload);
1971
+ switch (method) {
1972
+ case 'google':
1973
+ this.withGooglePay(response, payload);
1974
+ break
1975
+ case 'apple':
1976
+ this.withApplePay(response, payload);
1977
+ break
1861
1978
  }
1862
1979
  return response
1863
- .done(function (details) {
1864
- this.trigger('details', {
1865
- payment_system: this.payload.payment_system,
1866
- data: details,
1980
+ .done(function (data) {
1981
+ context.trigger('details', {
1982
+ payment_system: context.payload.payment_system,
1983
+ data,
1867
1984
  });
1868
1985
  })
1869
1986
  .fail(function (error) {
1870
- this.trigger('error', error);
1871
- if (this.params.embedded === true) {
1872
- location.reload();
1873
- this.trigger('reload', this.params);
1874
- }
1987
+ context.trigger('error', error);
1875
1988
  })
1876
1989
  },
1877
- makeNativePayment(defer, payload) {
1878
- const self = this;
1879
- const request = getPaymentRequest(payload.methods, payload.details, payload.options);
1880
- this.addEvent(request, 'merchantvalidation', 'merchantValidation');
1881
- request
1882
- .canMakePayment()
1883
- .then(function () {
1884
- request
1885
- .show()
1886
- .then(function (response) {
1887
- response.complete('success').then(function () {
1888
- defer.resolveWith(self, [response.details]);
1889
- });
1890
- })
1891
- .catch(function (e) {
1892
- defer.rejectWith(self, [{ code: e.code, message: e.message }]);
1893
- });
1894
- })
1895
- .catch(function (e) {
1896
- defer.rejectWith(self, [{ code: e.code, message: e.message }]);
1897
- });
1898
- },
1899
- makePaymentFallback(defer, methods) {
1900
- const self = this;
1901
- GooglePay.load().then(() => {
1902
- GooglePay.show(methods)
1903
- .then((details) => {
1904
- defer.resolveWith(self, [details]);
1905
- })
1906
- .catch((e) => {
1907
- defer.rejectWith(self, [{ code: e.code, message: e.message }]);
1908
- });
1909
- });
1910
- },
1911
- appleSession(params) {
1912
- const defer = Deferred();
1913
- this.request(
1914
- 'session',
1915
- params,
1916
- function (c, model) {
1917
- defer.resolveWith(this, [model.serialize()]);
1918
- },
1919
- function (c, model) {
1920
- defer.rejectWith(this, [model]);
1921
- }
1922
- );
1923
- return defer
1924
- },
1925
- merchantValidation(cx, event) {
1926
- const { validationURL } = event;
1927
- const { host } = location;
1928
- this.appleSession({
1929
- url: validationURL,
1930
- domain: host,
1931
- merchant_id: this.merchant,
1932
- })
1933
- .done(function (session) {
1934
- try {
1935
- event.complete(session.data);
1936
- } catch (error) {
1937
- this.trigger('error', error);
1938
- }
1939
- })
1940
- .fail(function (error) {
1941
- this.trigger('error', error);
1942
- });
1943
- },
1944
1990
  });
1945
1991
 
1946
1992
  const PaymentElement = Module.extend({
@@ -1956,11 +2002,22 @@ const PaymentElement = Module.extend({
1956
2002
  height: 38,
1957
2003
  },
1958
2004
  init(params) {
2005
+ this.state = {
2006
+ isSupported: false,
2007
+ isAllowed: false,
2008
+ isMounted: false,
2009
+ };
1959
2010
  this.params = {};
1960
2011
  this.utils.extend(this.params, this.defaults);
1961
2012
  this.utils.extend(this.params, params);
1962
2013
  this.initElement();
1963
2014
  },
2015
+ timeout(callback, timeout) {
2016
+ this.timeoutMap = this.timeoutMap || {};
2017
+ clearTimeout(this.timeoutMap[timeout]);
2018
+ this.timeoutMap[callback] = setTimeout(callback, timeout);
2019
+ return this
2020
+ },
1964
2021
  getElementUrl() {
1965
2022
  return [this.params.origin, this.params.endpoint].join('')
1966
2023
  },
@@ -2019,18 +2076,20 @@ const PaymentElement = Module.extend({
2019
2076
  },
2020
2077
  onClickFail() {},
2021
2078
  onSupported(cx, supported) {
2022
- if (supported.provider.includes(this.params.method)) {
2023
- this.mount();
2024
- }
2079
+ this.state.isSupported = supported.provider.includes(this.params.method);
2080
+ this.render();
2025
2081
  },
2026
2082
  onPayload(cx, payload) {
2027
- if (payload.allowed.includes(this.params.method)) {
2028
- this.show();
2029
- }
2083
+ this.state.isAllowed = payload.allowed.includes(this.params.method);
2084
+ this.render();
2030
2085
  },
2031
2086
  onPending(cx, state) {
2032
2087
  this.pending = state;
2033
- this.send('pending', { state: state });
2088
+ this.addCss(this.element, {
2089
+ transition: 'height 0.2s ease-out, opacity 0.4s ease-out',
2090
+ pointerEvents: this.pending ? 'none' : '',
2091
+ opacity: this.pending ? '0.5' : '1',
2092
+ });
2034
2093
  },
2035
2094
  initEvents() {
2036
2095
  this.addEvent(this.button, 'mouseenter', 'onEvent');
@@ -2057,36 +2116,50 @@ const PaymentElement = Module.extend({
2057
2116
  this.initEvents();
2058
2117
  return this
2059
2118
  },
2060
- mount() {
2061
- this.appendTo(this.params.appendTo);
2119
+ render() {
2120
+ if (this.state.isSupported === false) return this
2121
+ if (this.state.isAllowed === false) return this
2122
+ this.mount();
2123
+ this.show();
2124
+ },
2125
+ unmount() {
2126
+ if (this.element.parentNode) {
2127
+ this.element.parentNode.removeChild(this.element);
2128
+ }
2129
+ return this
2062
2130
  },
2063
- isMounted() {
2064
- return document.body.contains(this.element)
2131
+ mount() {
2132
+ if (document.body.contains(this.element) === false) {
2133
+ this.appendTo(this.params.appendTo);
2134
+ }
2135
+ return this
2065
2136
  },
2066
2137
  show() {
2067
- if (this.isMounted() === false) return this
2068
- this.addCss(this.iframe, {
2069
- transition: 'opacity 0.6s 0.4s ease-out',
2070
- opacity: this.utils.cssUnit(1),
2071
- });
2072
- this.addCss(this.element, {
2073
- transition: 'height 0.2s ease-out',
2074
- height: this.utils.cssUnit(this.params.height, 'px'),
2075
- });
2076
- this.trigger('show', {});
2138
+ this.timeout(() => {
2139
+ this.addCss(this.iframe, {
2140
+ transition: 'opacity 0.6s 0.4s ease-out',
2141
+ opacity: this.utils.cssUnit(1),
2142
+ });
2143
+ this.addCss(this.element, {
2144
+ transition: 'height 0.2s ease-out',
2145
+ height: this.utils.cssUnit(this.params.height, 'px'),
2146
+ });
2147
+ this.trigger('show', {});
2148
+ }, 25);
2077
2149
  return this
2078
2150
  },
2079
2151
  hide() {
2080
- if (this.isMounted() === false) return this
2081
- this.addCss(this.iframe, {
2082
- transition: 'opacity 0.4s ease-out',
2083
- opacity: this.utils.cssUnit(0),
2084
- });
2085
- this.addCss(this.element, {
2086
- transition: 'height 0.2s 0.4s ease-out',
2087
- height: this.utils.cssUnit(0, 'px'),
2088
- });
2089
- this.trigger('hide', {});
2152
+ this.timeout(() => {
2153
+ this.addCss(this.iframe, {
2154
+ transition: 'opacity 0.4s ease-out',
2155
+ opacity: this.utils.cssUnit(0),
2156
+ });
2157
+ this.addCss(this.element, {
2158
+ transition: 'height 0.2s 0.4s ease-out',
2159
+ height: this.utils.cssUnit(0, 'px'),
2160
+ });
2161
+ this.trigger('hide', {});
2162
+ }, 25);
2090
2163
  return this
2091
2164
  },
2092
2165
  });
@@ -2109,6 +2182,9 @@ const PaymentButton = Module.extend({
2109
2182
  before(defer) {
2110
2183
  defer.resolve();
2111
2184
  },
2185
+ after(defer, params) {
2186
+ defer.resolve(params);
2187
+ },
2112
2188
  },
2113
2189
  init(params) {
2114
2190
  this.elements = {};
@@ -2119,7 +2195,8 @@ const PaymentButton = Module.extend({
2119
2195
  origin: params.origin || this.defaults.origin,
2120
2196
  endpoint: this.utils.extend({}, this.defaults.endpoint, params.endpoint),
2121
2197
  style: this.utils.extend({}, this.defaults.style, params.style),
2122
- before: params.before || this.defaults.before,
2198
+ before: isFunction(params.before) ? params.before : this.defaults.before,
2199
+ after: isFunction(params.after) ? params.after : this.defaults.after,
2123
2200
  data: this.utils.extend({}, this.defaults.data, params.data),
2124
2201
  };
2125
2202
  this.initApi(params.api);
@@ -2144,10 +2221,12 @@ const PaymentButton = Module.extend({
2144
2221
  }
2145
2222
  },
2146
2223
  initPaymentRequestApi() {
2147
- this.request = new PaymentRequestApi();
2224
+ this.request = new PaymentRequestApi({
2225
+ before: this.params.before,
2226
+ after: this.params.after,
2227
+ });
2148
2228
  this.request.setApi(this.api);
2149
2229
  this.request.setMerchant(this.params.data.merchant_id);
2150
- this.request.setBeforeCallback(this.params.before);
2151
2230
  this.request.on('details', this.proxy('onDetails'));
2152
2231
  this.request.on('error', this.proxy('onError'));
2153
2232
  },
@@ -2184,10 +2263,16 @@ const PaymentButton = Module.extend({
2184
2263
  },
2185
2264
  onDetails(cx, data) {
2186
2265
  this.api.scope(() => {
2187
- this.api
2188
- .request('api.checkout.form', 'request', this.utils.extend({}, this.params.data, data))
2189
- .done(this.proxy('onSuccess'))
2190
- .fail(this.proxy('onError'));
2266
+ this.request.after(this.params.data).done((extendParams) => {
2267
+ this.api
2268
+ .request(
2269
+ 'api.checkout.form',
2270
+ 'request',
2271
+ this.utils.extend({}, this.params.data, extendParams || {}, data)
2272
+ )
2273
+ .done(this.proxy('onSuccess'))
2274
+ .fail(this.proxy('onError'));
2275
+ });
2191
2276
  });
2192
2277
  },
2193
2278
  onSuccess(cx, data) {