@flittpayments/js-sdk 2.0.11 → 2.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -83,22 +83,66 @@ git clone git@github.com:flittpayments/js-sdk.git
83
83
  <script src="https://cdn.jsdelivr.net/npm/@flittpayments/js-sdk"></script>
84
84
  <div class="payment-button-container"></div>
85
85
  <script>
86
- $checkout
86
+ const paymentButton = $checkout
87
87
  .get('PaymentButton', {
88
+ // Target DOM selector where the button will be rendered
88
89
  element: '.payment-button-container',
90
+ // Visual appearance customization
89
91
  style: {
90
- type: 'long', // short|long
91
- color: 'black', // black|white
92
- height: 38, // button height
92
+ type: 'long', // Options: 'short' | 'long'
93
+ color: 'black', // Options: 'white-outline' | 'white' | 'black'
94
+ mode: 'donate', // Options: 'default' | 'plain' | 'donate' | 'checkout' | 'order' | 'pay' | 'buy' | 'subscribe' | 'book'
95
+ height: 38, // Button height in pixels
93
96
  },
97
+ // Payload or metadata required for the transaction
94
98
  data: { Parameters },
95
99
  })
96
- .on('success', function (model) {
100
+ .on('success', (model) => {
97
101
  console.log('success', model)
98
102
  })
99
- .on('error', function (model) {
103
+ .on('error', (model) => {
100
104
  console.log('error', model)
101
105
  })
106
+ // Visibility event handlers
107
+ paymentButton
108
+ .on('show', () => {
109
+ // Fires immediately when the showing process starts
110
+ })
111
+ .on('shown', () => {
112
+ // Fires after the show transition/animation ends
113
+ })
114
+ .on('hide', () => {
115
+ // Fires immediately when the hiding process starts
116
+ })
117
+ .on('hidden', () => {
118
+ // Fires after the hide transition/animation ends
119
+ })
120
+ // Apple Pay Specific Events
121
+ .on('show:apple', () => {
122
+ // Fires immediately when Apple Pay starts showing
123
+ })
124
+ .on('shown:apple', () => {
125
+ // Fires after Apple Pay show transition ends
126
+ })
127
+ .on('hide:apple', () => {
128
+ // Fires immediately when Apple Pay starts hiding
129
+ })
130
+ .on('hidden:apple', () => {
131
+ // Fires after Apple Pay hide transition ends
132
+ })
133
+ // Google Pay Specific Events
134
+ .on('show:google', () => {
135
+ // Fires immediately when Google Pay starts showing
136
+ })
137
+ .on('shown:google', () => {
138
+ // Fires after Google Pay show transition ends
139
+ })
140
+ .on('hide:google', () => {
141
+ // Fires immediately when Google Pay starts hiding
142
+ })
143
+ .on('hidden:google', () => {
144
+ // Fires after Google Pay hide transition ends
145
+ })
102
146
  </script>
103
147
  </body>
104
148
  </html>
@@ -83,6 +83,9 @@ const isFunction = o => {
83
83
  const isArray = o => {
84
84
  return getType(o) === 'array';
85
85
  };
86
+ const isBoolean = o => {
87
+ return typeof o === 'boolean';
88
+ };
86
89
  const isElement = o => {
87
90
  return o && o.nodeType === 1;
88
91
  };
@@ -426,6 +429,13 @@ const loadExternalApi = (url, path) => {
426
429
  document.head.appendChild(script);
427
430
  return promise;
428
431
  };
432
+ const transitionDelay = el => {
433
+ let {
434
+ transitionDelay,
435
+ transitionDuration
436
+ } = getComputedStyle(el);
437
+ return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * 1000;
438
+ };
429
439
 
430
440
  var Utils = /*#__PURE__*/Object.freeze({
431
441
  __proto__: null,
@@ -448,6 +458,7 @@ var Utils = /*#__PURE__*/Object.freeze({
448
458
  hasProp: hasProp,
449
459
  isArguments: isArguments,
450
460
  isArray: isArray,
461
+ isBoolean: isBoolean,
451
462
  isDate: isDate,
452
463
  isElement: isElement,
453
464
  isError: isError,
@@ -468,6 +479,7 @@ var Utils = /*#__PURE__*/Object.freeze({
468
479
  removeElement: removeElement,
469
480
  stringFormat: stringFormat,
470
481
  toArray: toArray,
482
+ transitionDelay: transitionDelay,
471
483
  uuid: uuid
472
484
  });
473
485
 
@@ -765,6 +777,14 @@ function Deferred() {
765
777
  isProgress() {
766
778
  return status === PROGRESS;
767
779
  },
780
+ clear() {
781
+ if (this.isPending()) {
782
+ resolveCallbacks.length = 0;
783
+ rejectCallbacks.length = 0;
784
+ notifyCallbacks.length = 0;
785
+ }
786
+ return this;
787
+ },
768
788
  resolveWith(context, args) {
769
789
  if (this.isPending()) {
770
790
  status = RESOLVED;
@@ -1679,6 +1699,9 @@ const PaymentRequestApi = Module.extend({
1679
1699
  setMerchant(merchant) {
1680
1700
  this.merchant = merchant;
1681
1701
  },
1702
+ getProviderNames() {
1703
+ return this.supported.provider.filter(name => !!~this.payload.allowed.indexOf(name));
1704
+ },
1682
1705
  setBeforeCallback(beforeCallback) {
1683
1706
  if (isFunction(beforeCallback)) {
1684
1707
  this.params.before = beforeCallback;
@@ -1722,29 +1745,28 @@ const PaymentRequestApi = Module.extend({
1722
1745
  update(data) {
1723
1746
  if (this.isPending()) return;
1724
1747
  this.setPending(true);
1725
- const context = this;
1726
- const defer = Deferred();
1727
1748
  this.params.data = data;
1728
- this.request('api.checkout.pay', 'methods', this.params.data, function (model) {
1729
- context.setPending(false);
1730
- context.setPayload(model.serialize());
1731
- defer.resolve(model);
1732
- }, function (model) {
1733
- context.setPending(false);
1734
- context.trigger('error', model);
1735
- defer.reject(model);
1736
- });
1737
- return defer;
1749
+ this.defer = Deferred();
1750
+ this.request('api.checkout.pay', 'methods', this.params.data, this.proxy(function (_cx, model) {
1751
+ this.setPending(false);
1752
+ this.setPayload(model.serialize());
1753
+ this.defer.resolve(model);
1754
+ }), this.proxy(function (_cx, model) {
1755
+ this.setPending(false);
1756
+ this.setPayload(model.serialize());
1757
+ this.trigger('error', model.serialize());
1758
+ this.defer.reject(model);
1759
+ }));
1760
+ return this.defer;
1738
1761
  },
1739
1762
  isPending() {
1740
1763
  return this.pendingState === true;
1741
1764
  },
1742
1765
  setPending(state) {
1743
- this.pendingState = state;
1744
- clearTimeout(this.pendingTimeoutEvent);
1745
- this.pendingTimeoutEvent = setTimeout(function (context) {
1746
- context.trigger('pending', state);
1747
- }, 50, this);
1766
+ if (this.pendingState !== state) {
1767
+ this.pendingState = state;
1768
+ this.trigger('pending', state);
1769
+ }
1748
1770
  },
1749
1771
  before() {
1750
1772
  if (this.isPending()) return;
@@ -1854,9 +1876,12 @@ const PaymentElement = Module.extend({
1854
1876
  },
1855
1877
  init(params) {
1856
1878
  this.state = {
1857
- isSupported: false,
1858
- isAllowed: false,
1859
- isMounted: false
1879
+ type: 'hide',
1880
+ transition: false,
1881
+ pending: false,
1882
+ mounted: false,
1883
+ supported: false,
1884
+ allowed: false
1860
1885
  };
1861
1886
  this.params = {};
1862
1887
  this.utils.extend(this.params, this.defaults);
@@ -1866,7 +1891,7 @@ const PaymentElement = Module.extend({
1866
1891
  timeout(callback, timeout) {
1867
1892
  this.timeoutMap = this.timeoutMap || {};
1868
1893
  clearTimeout(this.timeoutMap[timeout]);
1869
- this.timeoutMap[callback] = setTimeout(callback, timeout);
1894
+ this.timeoutMap[callback] = setTimeout(this.proxy(callback), timeout);
1870
1895
  return this;
1871
1896
  },
1872
1897
  getElementUrl() {
@@ -1884,6 +1909,7 @@ const PaymentElement = Module.extend({
1884
1909
  initElement() {
1885
1910
  this.element = this.utils.createElement('div');
1886
1911
  this.iframe = this.utils.createElement('iframe');
1912
+ this.iframeLoaded = Deferred();
1887
1913
  this.button = this.utils.createElement('a');
1888
1914
  this.addCss(this.element, ButtonContainerCss);
1889
1915
  this.addCss(this.button, ButtonCoverCss);
@@ -1901,8 +1927,11 @@ const PaymentElement = Module.extend({
1901
1927
  this.element.appendChild(this.iframe);
1902
1928
  this.element.appendChild(this.button);
1903
1929
  },
1904
- errorConnector() {},
1930
+ errorConnector() {
1931
+ this.iframeLoaded.resolveWith(this);
1932
+ },
1905
1933
  onloadConnector() {
1934
+ this.iframeLoaded.resolveWith(this);
1906
1935
  this.connector = new Connector({
1907
1936
  target: this.iframe.contentWindow,
1908
1937
  origin: this.params.origin
@@ -1915,13 +1944,13 @@ const PaymentElement = Module.extend({
1915
1944
  },
1916
1945
  onEvent(cx, ev) {
1917
1946
  ev.preventDefault();
1918
- if (this.pending) return false;
1947
+ if (this.state.pending) return false;
1919
1948
  this.send('event', {
1920
1949
  type: ev.type
1921
1950
  });
1922
1951
  },
1923
1952
  onClick() {
1924
- if (this.pending) return false;
1953
+ if (this.state.pending) return false;
1925
1954
  this.request.before().done(this.proxy('onClickDone')).fail(this.proxy('onClickFail'));
1926
1955
  },
1927
1956
  onClickDone() {
@@ -1929,19 +1958,19 @@ const PaymentElement = Module.extend({
1929
1958
  },
1930
1959
  onClickFail() {},
1931
1960
  onSupported(cx, supported) {
1932
- this.state.isSupported = supported.provider.includes(this.params.method);
1961
+ this.state.supported = supported.provider.includes(this.params.method);
1933
1962
  this.render();
1934
1963
  },
1935
1964
  onPayload(cx, payload) {
1936
- this.state.isAllowed = payload.allowed.includes(this.params.method);
1965
+ this.state.allowed = payload.allowed.includes(this.params.method);
1937
1966
  this.render();
1938
1967
  },
1939
1968
  onPending(cx, state) {
1940
- this.pending = state;
1969
+ this.state.pending = state;
1941
1970
  this.addCss(this.element, {
1942
1971
  transition: 'height 0.2s ease-out, opacity 0.4s ease-out',
1943
- pointerEvents: this.pending ? 'none' : '',
1944
- opacity: this.pending ? '0.5' : '1'
1972
+ pointerEvents: this.state.pending ? 'none' : '',
1973
+ opacity: this.state.pending ? '0.5' : '1'
1945
1974
  });
1946
1975
  },
1947
1976
  initEvents() {
@@ -1953,10 +1982,10 @@ const PaymentElement = Module.extend({
1953
1982
  },
1954
1983
  setPaymentRequest(request) {
1955
1984
  if (!(request instanceof PaymentRequestApi)) throw Error('request is not instance of PaymentRequestApi');
1956
- this.request = request;
1957
1985
  const onSupported = this.proxy('onSupported');
1958
1986
  const onPayload = this.proxy('onPayload');
1959
1987
  const onPending = this.proxy('onPending');
1988
+ this.request = request;
1960
1989
  this.request.off('supported', onSupported).on('supported', onSupported);
1961
1990
  this.request.off('payload', onPayload).on('payload', onPayload);
1962
1991
  this.request.off('pending', onPending).on('pending', onPending);
@@ -1969,50 +1998,95 @@ const PaymentElement = Module.extend({
1969
1998
  return this;
1970
1999
  },
1971
2000
  render() {
1972
- if (this.state.isSupported === false) return this;
1973
- if (this.state.isAllowed === false) return this;
2001
+ if (this.state.supported === false) return this.hide();
2002
+ if (this.state.allowed === false) return this.hide();
1974
2003
  this.mount();
1975
2004
  this.show();
1976
2005
  },
2006
+ notMounted() {
2007
+ return this.state.mounted === false;
2008
+ },
2009
+ isMounted() {
2010
+ return this.state.mounted === true;
2011
+ },
1977
2012
  unmount() {
1978
2013
  if (this.element.parentNode) {
1979
2014
  this.element.parentNode.removeChild(this.element);
2015
+ this.state.mounted = false;
1980
2016
  }
1981
2017
  return this;
1982
2018
  },
1983
2019
  mount() {
1984
- if (document.body.contains(this.element) === false) {
2020
+ if (!document.body.contains(this.element)) {
1985
2021
  this.appendTo(this.params.appendTo);
2022
+ this.state.mounted = true;
1986
2023
  }
1987
2024
  return this;
1988
2025
  },
2026
+ transitionValue(value) {
2027
+ return value;
2028
+ },
2029
+ getTimeoutValue() {
2030
+ return toArray(arguments).map(transitionDelay).sort().pop();
2031
+ },
2032
+ getState(state, transition) {
2033
+ return this.state.type === (state ? 'show' : 'hide') && this.state.transition === transition;
2034
+ },
2035
+ triggerState() {
2036
+ this.request.trigger(this.state.type, {
2037
+ transition: this.state.transition,
2038
+ method: this.params.method
2039
+ });
2040
+ },
1989
2041
  show() {
1990
- this.timeout(() => {
1991
- this.addCss(this.iframe, {
1992
- transition: 'opacity 0.6s 0.4s ease-out',
1993
- opacity: this.utils.cssUnit(1)
1994
- });
1995
- this.addCss(this.element, {
1996
- transition: 'height 0.2s ease-out',
1997
- height: this.utils.cssUnit(this.params.height, 'px')
1998
- });
1999
- this.trigger('show', {});
2000
- }, 25);
2042
+ if (this.notMounted()) return;
2043
+ this.timeout('showCallback', 25);
2001
2044
  return this;
2002
2045
  },
2003
2046
  hide() {
2004
- this.timeout(() => {
2005
- this.addCss(this.iframe, {
2006
- transition: 'opacity 0.4s ease-out',
2007
- opacity: this.utils.cssUnit(0)
2008
- });
2009
- this.addCss(this.element, {
2010
- transition: 'height 0.2s 0.4s ease-out',
2011
- height: this.utils.cssUnit(0, 'px')
2012
- });
2013
- this.trigger('hide', {});
2014
- }, 25);
2047
+ if (this.notMounted()) return;
2048
+ this.timeout('hideCallback', 25);
2015
2049
  return this;
2050
+ },
2051
+ showCallback() {
2052
+ this.addCss(this.element, {
2053
+ transition: this.transitionValue('height 0.2s ease-out'),
2054
+ height: this.utils.cssUnit(this.params.height, 'px'),
2055
+ 'will-change': 'height'
2056
+ });
2057
+ this.addCss(this.iframe, {
2058
+ transition: this.transitionValue('opacity 0.225s 0.225s ease-out'),
2059
+ opacity: this.utils.cssUnit(1),
2060
+ 'will-change': 'opacity'
2061
+ });
2062
+ this.afterCallback('show');
2063
+ },
2064
+ hideCallback() {
2065
+ this.addCss(this.element, {
2066
+ transition: this.transitionValue('height 0.225s 0.225s ease-out'),
2067
+ height: this.utils.cssUnit(0, 'px'),
2068
+ 'will-change': 'height'
2069
+ });
2070
+ this.addCss(this.iframe, {
2071
+ transition: this.transitionValue('opacity 0.225s ease-out'),
2072
+ opacity: this.utils.cssUnit(0),
2073
+ 'will-change': 'opacity'
2074
+ });
2075
+ this.afterCallback('hide');
2076
+ },
2077
+ afterCallback(state) {
2078
+ if (this.state.type === state) return;
2079
+ if (this.state.transition) return;
2080
+ this.state.transition = true;
2081
+ this.state.type = state;
2082
+ this.triggerState();
2083
+ this.timeout('afterTransition', this.getTimeoutValue(this.iframe, this.element));
2084
+ },
2085
+ afterTransition() {
2086
+ this.state.transition = false;
2087
+ this.iframeLoaded.then(function () {
2088
+ this.triggerState();
2089
+ });
2016
2090
  }
2017
2091
  });
2018
2092
 
@@ -2082,16 +2156,21 @@ const PaymentButton = Module.extend({
2082
2156
  });
2083
2157
  this.request.setApi(this.api);
2084
2158
  this.request.setMerchant(this.params.data.merchant_id);
2159
+ this.request.on('pending', this.proxy('onPending'));
2085
2160
  this.request.on('details', this.proxy('onDetails'));
2086
2161
  this.request.on('error', this.proxy('onError'));
2162
+ this.request.on('show', this.proxy('onShow'));
2163
+ this.request.on('hide', this.proxy('onHide'));
2087
2164
  },
2088
2165
  initElements() {
2166
+ const self = this;
2089
2167
  const style = this.params.style;
2090
2168
  const data = this.params.data;
2091
2169
  const origin = this.params.origin;
2092
2170
  const appendTo = this.params.element;
2093
2171
  const endpoint = this.params.endpoint.element;
2094
2172
  const request = this.request;
2173
+ this.buttons = [];
2095
2174
  this.container = this.utils.querySelector(this.params.element);
2096
2175
  this.addCss(this.container, {
2097
2176
  display: 'flex',
@@ -2110,24 +2189,51 @@ const PaymentButton = Module.extend({
2110
2189
  height: style.height
2111
2190
  });
2112
2191
  element.setPaymentRequest(request);
2192
+ self.buttons.push(element);
2113
2193
  });
2114
2194
  request.getSupportedMethods();
2115
2195
  },
2116
2196
  update(data) {
2117
2197
  return this.request.update(this.utils.extend(this.params.data, data || {}));
2118
2198
  },
2119
- onDetails(cx, data) {
2199
+ onDetails(_cx, data) {
2120
2200
  this.api.scope(() => {
2121
2201
  this.request.after(this.params.data).done(extendParams => {
2122
2202
  this.api.request('api.checkout.form', 'request', this.utils.extend({}, this.params.data, extendParams || {}, data)).done(this.proxy('onSuccess')).fail(this.proxy('onError'));
2123
2203
  });
2124
2204
  });
2125
2205
  },
2126
- onSuccess(cx, data) {
2206
+ onSuccess(_cx, data) {
2127
2207
  this.trigger('success', data);
2128
2208
  },
2129
- onError(cx, data) {
2209
+ onError(_cx, data) {
2130
2210
  this.trigger('error', data);
2211
+ },
2212
+ toggleEventType(state, transition) {
2213
+ return state ? transition ? 'show' : 'shown' : transition ? 'hide' : 'hidden';
2214
+ },
2215
+ toggleEventNamespace(state, data) {
2216
+ const name = [];
2217
+ name.push(this.toggleEventType(state, data.transition));
2218
+ name.push(data.method);
2219
+ return name.join(':');
2220
+ },
2221
+ triggerEventComplete(state, data) {
2222
+ const transition = data.transition;
2223
+ const name = this.toggleEventType(state, transition);
2224
+ const check = this.buttons.filter(i => i.isMounted()).every(i => i.getState(state, transition));
2225
+ if (check) this.trigger(name, {});
2226
+ },
2227
+ onShow(_cx, data) {
2228
+ this.trigger(this.toggleEventNamespace(true, data), {});
2229
+ this.triggerEventComplete(true, data);
2230
+ },
2231
+ onHide(_cx, data) {
2232
+ this.trigger(this.toggleEventNamespace(false, data), {});
2233
+ this.triggerEventComplete(false, data);
2234
+ },
2235
+ onPending(_cx, state) {
2236
+ this.trigger('pending', state);
2131
2237
  }
2132
2238
  });
2133
2239