@flittpayments/js-sdk 2.0.5 → 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;
@@ -1345,6 +1512,7 @@ const Api = Module.extend({
1345
1512
  }
1346
1513
  },
1347
1514
  init(params) {
1515
+ this.iframeDeferred = Deferred();
1348
1516
  this.initParams(params);
1349
1517
  },
1350
1518
  url(type, url) {
@@ -1358,8 +1526,6 @@ const Api = Module.extend({
1358
1526
  this.params = this.utils.extend({}, this.defaults);
1359
1527
  this.extendParams(params);
1360
1528
  this.setOrigin(this.params.origin);
1361
- this.loaded = false;
1362
- this.created = false;
1363
1529
  },
1364
1530
  setOrigin(origin) {
1365
1531
  if (this.utils.isString(origin)) {
@@ -1370,16 +1536,21 @@ const Api = Module.extend({
1370
1536
  scope(callback) {
1371
1537
  callback = this.proxy(callback);
1372
1538
  this.domReady(function () {
1373
- if (this.createFrame().loaded === true) {
1374
- callback();
1539
+ this.createFrame();
1540
+ if (this.iframeDeferred.isPending()) {
1541
+ this.iframeDeferred.done(callback);
1375
1542
  } else {
1376
- this.on('checkout.api', callback);
1543
+ callback();
1377
1544
  }
1378
1545
  });
1379
1546
  },
1380
1547
  domReady(callback) {
1381
1548
  callback = this.proxy(callback);
1382
- document.readyState !== 'loading' ? callback() : window.addEventListener('DOMContentLoaded', callback);
1549
+ if (document.readyState === 'loading') {
1550
+ window.addEventListener('DOMContentLoaded', callback);
1551
+ } else {
1552
+ callback();
1553
+ }
1383
1554
  },
1384
1555
  request(model, method, params) {
1385
1556
  const defer = Deferred();
@@ -1405,7 +1576,8 @@ const Api = Module.extend({
1405
1576
  }));
1406
1577
  return defer;
1407
1578
  },
1408
- loadFrame(url) {
1579
+ createFrame() {
1580
+ if (this.iframe) return this;
1409
1581
  this.iframe = this.utils.createElement('iframe');
1410
1582
  this.addAttr(this.iframe, {
1411
1583
  allowtransparency: true,
@@ -1413,31 +1585,29 @@ const Api = Module.extend({
1413
1585
  scrolling: 'no'
1414
1586
  });
1415
1587
  this.addAttr(this.iframe, {
1416
- src: url
1588
+ src: this.url('gateway')
1417
1589
  });
1418
1590
  this.addCss(this.iframe, ApiFrameCss);
1419
- this.container = this.utils.querySelector(this.params.container);
1591
+ if (this.utils.isElement(this.params.container)) {
1592
+ this.container = this.params.container;
1593
+ }
1594
+ if (this.utils.isString(this.params.container)) {
1595
+ this.container = this.utils.querySelector(this.params.container);
1596
+ }
1420
1597
  if (this.container) {
1421
1598
  if (this.container.firstChild) {
1422
1599
  this.container.insertBefore(this.iframe, this.container.firstChild);
1423
1600
  } else {
1424
1601
  this.container.appendChild(this.iframe);
1425
1602
  }
1426
- } else {
1427
- throw Error(`container element not found: querySelector("${this.params.container}")`);
1428
- }
1429
- return this.iframe;
1430
- },
1431
- createFrame() {
1432
- if (this.created === false) {
1433
- this.created = true;
1434
- this.iframe = this.loadFrame(this.url('gateway'));
1435
1603
  this.connector = new Connector({
1436
1604
  target: this.iframe.contentWindow,
1437
1605
  origin: this.params.origin
1438
1606
  });
1439
1607
  this.connector.on('load', this.proxy('onLoadConnector'));
1440
1608
  this.connector.on('modal', this.proxy('onOpenModal'));
1609
+ } else {
1610
+ throw Error(`container element not found: document.querySelector("${this.params.container}")`);
1441
1611
  }
1442
1612
  return this;
1443
1613
  },
@@ -1452,125 +1622,36 @@ const Api = Module.extend({
1452
1622
  this.trigger('modal.close', modal, data);
1453
1623
  },
1454
1624
  onLoadConnector() {
1455
- this.loaded = true;
1625
+ this.iframeDeferred.resolve(true);
1456
1626
  this.connector.off('load');
1457
- this.trigger('checkout.api');
1458
- this.off('checkout.api');
1459
- }
1460
- });
1461
-
1462
- const GooglePayClient = Module.extend({
1463
- id: 'google-payments-api',
1464
- init() {
1465
- this.client = null;
1466
- this.wrapper = this.utils.querySelector('head');
1467
- this.defer = Deferred();
1468
- },
1469
- load() {
1470
- if (this.utils.getPath('google.payments.api.PaymentsClient')) {
1471
- return this.defer.resolveWith(this);
1472
- }
1473
- if (this.utils.querySelector('#'.concat(this.id))) {
1474
- return this.defer;
1475
- }
1476
- this.script = this.utils.createElement('script');
1477
- this.addAttr(this.script, {
1478
- id: this.id,
1479
- async: true,
1480
- src: GooglePayApi
1481
- });
1482
- this.utils.isElement(this.wrapper) && this.wrapper.appendChild(this.script);
1483
- this.addEvent(this.script, 'load', 'onLoadSuccess');
1484
- this.addEvent(this.script, 'error', 'onLoadError');
1485
- return this.defer;
1486
- },
1487
- show(methods) {
1488
- const method = methods.find(function (item) {
1489
- return item.supportedMethods === 'https://google.com/pay';
1490
- });
1491
- const client = this.getClient({
1492
- environment: method.data.environment
1493
- });
1494
- return client.loadPaymentData(method.data);
1495
- },
1496
- readyToPay(cx, response) {
1497
- if (response.result) {
1498
- this.defer.resolveWith(this);
1499
- }
1500
- },
1501
- onError(cx, error) {
1502
- this.defer.rejectWith(this, error);
1503
- },
1504
- onLoadSuccess() {
1505
- const client = this.getClient();
1506
- if (client) client.isReadyToPay(GoogleBaseRequest).then(this.proxy('readyToPay')).catch(this.proxy('onError'));
1507
- },
1508
- onLoadError() {
1509
- this.defer.rejectWith(this);
1510
- },
1511
- getClient(options) {
1512
- if (options || this.client === null) {
1513
- const PaymentClient = this.utils.getPath('google.payments.api.PaymentsClient');
1514
- if (PaymentClient) {
1515
- this.client = new PaymentClient(options);
1516
- } else {
1517
- this.onError(null, new Error('Google Client Error'));
1518
- }
1519
- }
1520
- return this.client;
1521
1627
  }
1522
1628
  });
1523
- const GooglePay = new GooglePayClient();
1524
1629
 
1525
- const getPaymentMethods = () => {
1526
- return [['google', {
1527
- supportedMethods: 'https://google.com/pay',
1528
- data: GoogleBaseRequest
1529
- }], ['apple', {
1530
- supportedMethods: 'https://apple.com/apple-pay'
1531
- }, () => hasProp(window, 'ApplePaySession')]];
1532
- };
1533
- let requestDeferred = null;
1534
- let requestSupported = {
1535
- fallback: false,
1536
- provider: []
1537
- };
1630
+ let supportedMethodsDeferred = null;
1538
1631
  const getSupportedMethods = () => {
1539
- if (requestDeferred) return requestDeferred;
1540
- requestDeferred = Deferred();
1541
- const methods = getPaymentMethods();
1542
- const details = PaymentRequestDetails;
1543
- (function check() {
1544
- const item = methods.shift();
1545
- if (item === undefined) {
1546
- if (requestSupported.provider.indexOf('google') === -1) {
1547
- requestSupported.fallback = true;
1548
- 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);
1549
1644
  }
1550
- return requestDeferred.resolve(requestSupported);
1551
- }
1552
- const method = item.shift();
1553
- const config = item.shift();
1554
- const callback = item.shift();
1555
- if (isFunction(callback) && callback() === false) {
1556
- setTimeout(check, 25);
1557
- return false;
1558
- }
1559
- const request = getPaymentRequest([config], details, {});
1560
- if (request) {
1561
- request.canMakePayment().then(function (status) {
1562
- if (status === true) requestSupported.provider.push(method);
1563
- setTimeout(check, 25);
1564
- }).catch(function () {
1565
- setTimeout(check, 25);
1566
- });
1567
- } else {
1568
- setTimeout(check, 25);
1569
- }
1570
- })();
1571
- return requestDeferred;
1645
+ }));
1646
+ });
1647
+ Promise.all(promises).then(() => {
1648
+ supportedMethodsDeferred.resolve(supported);
1649
+ }, () => {
1650
+ supportedMethodsDeferred.resolve(supported);
1651
+ });
1652
+ return supportedMethodsDeferred;
1572
1653
  };
1573
- const PaymentRequestInterface = Module.extend({
1654
+ const PaymentRequestApi = Module.extend({
1574
1655
  config: {
1575
1656
  payment_system: '',
1576
1657
  fallback: false,
@@ -1609,6 +1690,16 @@ const PaymentRequestInterface = Module.extend({
1609
1690
  setMerchant(merchant) {
1610
1691
  this.merchant = merchant;
1611
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
+ },
1612
1703
  setApi(api) {
1613
1704
  if (api instanceof Api) this.api = api;
1614
1705
  return this;
@@ -1621,32 +1712,40 @@ const PaymentRequestInterface = Module.extend({
1621
1712
  },
1622
1713
  isFallbackMethod(method) {
1623
1714
  return method === 'google' && this.supported.fallback;
1624
- }
1625
- });
1626
- const PaymentRequestApi = PaymentRequestInterface.extend({
1627
- request(method, params, success, failure) {
1628
- if (this.api) {
1629
- this.api.scope(this.proxy(function () {
1630
- this.api.request('api.checkout.pay', method, params).done(this.proxy(success)).fail(this.proxy(failure));
1631
- }));
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
+ });
1632
1722
  }
1633
1723
  },
1634
- update(data) {
1724
+ session(params) {
1635
1725
  const defer = Deferred();
1636
- this.request('methods', data, function (cx, model) {
1637
- this.onUpdate(cx, model);
1638
- defer.resolveWith(this, [model]);
1639
- }, function (cx, model) {
1640
- this.onError(cx, model);
1641
- 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);
1642
1730
  });
1643
1731
  return defer;
1644
1732
  },
1645
- onUpdate(cx, model) {
1646
- this.setPayload(model.serialize());
1647
- },
1648
- onError(cx, model) {
1649
- 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;
1650
1749
  },
1651
1750
  isPending() {
1652
1751
  return this.pendingState === true;
@@ -1654,123 +1753,100 @@ const PaymentRequestApi = PaymentRequestInterface.extend({
1654
1753
  setPending(state) {
1655
1754
  this.pendingState = state;
1656
1755
  clearTimeout(this.pendingTimeoutEvent);
1657
- this.pendingTimeoutEvent = setTimeout(() => {
1658
- this.trigger('pending', state);
1659
- }, 100);
1660
- },
1661
- beforeCallback(defer) {
1662
- defer.resolve();
1663
- },
1664
- setBeforeCallback(callback) {
1665
- if (isFunction(callback)) {
1666
- this.beforeCallback = callback;
1667
- }
1668
- return this;
1756
+ this.pendingTimeoutEvent = setTimeout(function (context) {
1757
+ context.trigger('pending', state);
1758
+ }, 50, this);
1669
1759
  },
1670
1760
  before() {
1671
1761
  if (this.isPending()) return;
1672
1762
  this.setPending(true);
1673
1763
  const defer = Deferred();
1674
- defer.always(this.proxy(function () {
1675
- this.setPending(false);
1676
- }));
1677
- this.beforeCallback(defer);
1764
+ const context = this;
1765
+ defer.always(function () {
1766
+ context.setPending(false);
1767
+ });
1768
+ this.params.before(defer);
1678
1769
  return defer;
1679
1770
  },
1680
- pay(method) {
1681
- if (this.isPending()) return;
1682
- this.setPending(true);
1683
- const payload = this.getProviderPayload(method);
1684
- const response = Deferred();
1685
- response.always(function () {
1686
- this.setPending(false);
1687
- });
1688
- if (this.isMethodSupported(method) === false) {
1689
- return response.rejectWith(this, [{
1690
- test: true
1691
- }]);
1692
- }
1693
- if (this.isFallbackMethod(method)) {
1694
- this.makePaymentFallback(response, payload.methods);
1695
- } else {
1696
- this.makeNativePayment(response, payload);
1697
- }
1698
- return response.done(function (details) {
1699
- this.trigger('details', {
1700
- payment_system: this.payload.payment_system,
1701
- data: details
1702
- });
1703
- }).fail(function (error) {
1704
- this.trigger('error', error);
1705
- if (this.params.embedded === true) {
1706
- location.reload();
1707
- this.trigger('reload', this.params);
1708
- }
1709
- });
1771
+ after(params) {
1772
+ const defer = Deferred();
1773
+ this.params.after(defer, params);
1774
+ return defer;
1710
1775
  },
1711
- makeNativePayment(defer, payload) {
1712
- const self = this;
1713
- const request = getPaymentRequest(payload.methods, payload.details, payload.options);
1714
- this.addEvent(request, 'merchantvalidation', 'merchantValidation');
1715
- request.canMakePayment().then(function () {
1716
- request.show().then(function (response) {
1717
- response.complete('success').then(function () {
1718
- defer.resolveWith(self, [response.details]);
1719
- });
1720
- }).catch(function (e) {
1721
- defer.rejectWith(self, [{
1722
- code: e.code,
1723
- message: e.message
1724
- }]);
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
+ }
1725
1795
  });
1726
- }).catch(function (e) {
1727
- defer.rejectWith(self, [{
1728
- code: e.code,
1729
- message: e.message
1730
- }]);
1731
- });
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();
1732
1808
  },
1733
- makePaymentFallback(defer, methods) {
1734
- const self = this;
1735
- GooglePay.load().then(() => {
1736
- GooglePay.show(methods).then(details => {
1737
- defer.resolveWith(self, [details]);
1738
- }).catch(e => {
1739
- defer.rejectWith(self, [{
1740
- code: e.code,
1741
- message: e.message
1742
- }]);
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
1743
1823
  });
1744
1824
  });
1745
1825
  },
1746
- appleSession(params) {
1747
- const defer = Deferred();
1748
- this.request('session', params, function (c, model) {
1749
- defer.resolveWith(this, [model.serialize()]);
1750
- }, function (c, model) {
1751
- 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);
1752
1834
  });
1753
- return defer;
1754
- },
1755
- merchantValidation(cx, event) {
1756
- const {
1757
- validationURL
1758
- } = event;
1759
- const {
1760
- host
1761
- } = location;
1762
- this.appleSession({
1763
- url: validationURL,
1764
- domain: host,
1765
- merchant_id: this.merchant
1766
- }).done(function (session) {
1767
- try {
1768
- event.complete(session.data);
1769
- } catch (error) {
1770
- this.trigger('error', error);
1771
- }
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
+ });
1772
1848
  }).fail(function (error) {
1773
- this.trigger('error', error);
1849
+ context.trigger('error', error);
1774
1850
  });
1775
1851
  }
1776
1852
  });
@@ -1788,11 +1864,22 @@ const PaymentElement = Module.extend({
1788
1864
  height: 38
1789
1865
  },
1790
1866
  init(params) {
1867
+ this.state = {
1868
+ isSupported: false,
1869
+ isAllowed: false,
1870
+ isMounted: false
1871
+ };
1791
1872
  this.params = {};
1792
1873
  this.utils.extend(this.params, this.defaults);
1793
1874
  this.utils.extend(this.params, params);
1794
1875
  this.initElement();
1795
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
+ },
1796
1883
  getElementUrl() {
1797
1884
  return [this.params.origin, this.params.endpoint].join('');
1798
1885
  },
@@ -1853,19 +1940,18 @@ const PaymentElement = Module.extend({
1853
1940
  },
1854
1941
  onClickFail() {},
1855
1942
  onSupported(cx, supported) {
1856
- if (supported.provider.includes(this.params.method)) {
1857
- this.mount();
1858
- }
1943
+ this.state.isSupported = supported.provider.includes(this.params.method);
1859
1944
  },
1860
1945
  onPayload(cx, payload) {
1861
- if (payload.allowed.includes(this.params.method)) {
1862
- this.show();
1863
- }
1946
+ this.state.isAllowed = payload.allowed.includes(this.params.method);
1947
+ this.render();
1864
1948
  },
1865
1949
  onPending(cx, state) {
1866
1950
  this.pending = state;
1867
- this.send('pending', {
1868
- 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'
1869
1955
  });
1870
1956
  },
1871
1957
  initEvents() {
@@ -1892,36 +1978,50 @@ const PaymentElement = Module.extend({
1892
1978
  this.initEvents();
1893
1979
  return this;
1894
1980
  },
1895
- mount() {
1896
- 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();
1897
1986
  },
1898
- isMounted() {
1899
- 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;
1900
1998
  },
1901
1999
  show() {
1902
- if (this.isMounted() === false) return this;
1903
- this.addCss(this.iframe, {
1904
- transition: 'opacity 0.6s 0.4s ease-out',
1905
- opacity: this.utils.cssUnit(1)
1906
- });
1907
- this.addCss(this.element, {
1908
- transition: 'height 0.2s ease-out',
1909
- height: this.utils.cssUnit(this.params.height, 'px')
1910
- });
1911
- 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);
1912
2011
  return this;
1913
2012
  },
1914
2013
  hide() {
1915
- if (this.isMounted() === false) return this;
1916
- this.addCss(this.iframe, {
1917
- transition: 'opacity 0.4s ease-out',
1918
- opacity: this.utils.cssUnit(0)
1919
- });
1920
- this.addCss(this.element, {
1921
- transition: 'height 0.2s 0.4s ease-out',
1922
- height: this.utils.cssUnit(0, 'px')
1923
- });
1924
- 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);
1925
2025
  return this;
1926
2026
  }
1927
2027
  });
@@ -1943,6 +2043,9 @@ const PaymentButton = Module.extend({
1943
2043
  },
1944
2044
  before(defer) {
1945
2045
  defer.resolve();
2046
+ },
2047
+ after(defer, params) {
2048
+ defer.resolve(params);
1946
2049
  }
1947
2050
  },
1948
2051
  init(params) {
@@ -1954,7 +2057,8 @@ const PaymentButton = Module.extend({
1954
2057
  origin: params.origin || this.defaults.origin,
1955
2058
  endpoint: this.utils.extend({}, this.defaults.endpoint, params.endpoint),
1956
2059
  style: this.utils.extend({}, this.defaults.style, params.style),
1957
- 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,
1958
2062
  data: this.utils.extend({}, this.defaults.data, params.data)
1959
2063
  };
1960
2064
  this.initApi(params.api);
@@ -1979,10 +2083,12 @@ const PaymentButton = Module.extend({
1979
2083
  }
1980
2084
  },
1981
2085
  initPaymentRequestApi() {
1982
- this.request = new PaymentRequestApi();
2086
+ this.request = new PaymentRequestApi({
2087
+ before: this.params.before,
2088
+ after: this.params.after
2089
+ });
1983
2090
  this.request.setApi(this.api);
1984
2091
  this.request.setMerchant(this.params.data.merchant_id);
1985
- this.request.setBeforeCallback(this.params.before);
1986
2092
  this.request.on('details', this.proxy('onDetails'));
1987
2093
  this.request.on('error', this.proxy('onError'));
1988
2094
  },
@@ -2019,7 +2125,9 @@ const PaymentButton = Module.extend({
2019
2125
  },
2020
2126
  onDetails(cx, data) {
2021
2127
  this.api.scope(() => {
2022
- 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
+ });
2023
2131
  });
2024
2132
  },
2025
2133
  onSuccess(cx, data) {