@rebilly/instruments 1.0.2-beta.8 → 2.1.1-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. package/.babelrc +13 -4
  2. package/.eslintrc.js +3 -0
  3. package/.prettierrc.js +11 -0
  4. package/README.md +15 -314
  5. package/dist/events/base-event.js +6 -9
  6. package/dist/events/events.spec.js +4 -4
  7. package/dist/events/index.js +2 -1
  8. package/dist/functions/destroy.js +12 -14
  9. package/dist/functions/destroy.spec.js +3 -3
  10. package/dist/functions/mount/fetch-data.js +183 -0
  11. package/dist/functions/mount/fetch-data.spec.js +189 -0
  12. package/dist/functions/mount/index.js +158 -251
  13. package/dist/functions/mount/mount.spec.js +24 -121
  14. package/dist/functions/mount/setup-element.js +40 -0
  15. package/dist/functions/mount/setup-framepay.js +46 -0
  16. package/dist/functions/mount/setup-i18n.js +33 -0
  17. package/dist/functions/mount/setup-options.js +96 -0
  18. package/dist/functions/mount/setup-options.spec.js +66 -0
  19. package/dist/functions/mount/setup-storefront.js +34 -0
  20. package/dist/functions/mount/setup-styles.js +43 -0
  21. package/dist/functions/on.js +13 -4
  22. package/dist/functions/on.spec.js +19 -5
  23. package/dist/functions/purchase.js +139 -22
  24. package/dist/functions/purchase.spec.js +23 -19
  25. package/dist/functions/setup.js +85 -0
  26. package/dist/functions/setup.spec.js +87 -0
  27. package/dist/functions/show.js +31 -14
  28. package/dist/functions/show.spec.js +47 -18
  29. package/dist/functions/update.js +53 -27
  30. package/dist/functions/update.spec.js +40 -21
  31. package/dist/i18n/en.json +4 -1
  32. package/dist/i18n/es.json +4 -1
  33. package/dist/index.js +67 -56
  34. package/dist/index.spec.js +7 -27
  35. package/dist/loader/index.js +4 -3
  36. package/dist/storefront/index.js +33 -0
  37. package/dist/storefront/invoices.js +27 -0
  38. package/dist/storefront/models/base-model.js +18 -0
  39. package/dist/storefront/models/invoice-model.js +14 -0
  40. package/dist/storefront/models/plan-model.js +4 -35
  41. package/dist/storefront/models/product-model.js +4 -23
  42. package/dist/storefront/models/summary-model.js +12 -25
  43. package/dist/storefront/models/transaction-model.js +31 -0
  44. package/dist/storefront/payment-instruments.js +47 -0
  45. package/dist/storefront/payment-instruments.spec.js +55 -0
  46. package/dist/storefront/plans.js +15 -24
  47. package/dist/storefront/plans.spec.js +17 -44
  48. package/dist/storefront/products.js +16 -20
  49. package/dist/storefront/products.spec.js +25 -49
  50. package/dist/storefront/purchase.js +28 -16
  51. package/dist/storefront/purchase.spec.js +4 -22
  52. package/dist/storefront/ready-to-pay.js +26 -22
  53. package/dist/storefront/ready-to-pay.spec.js +25 -54
  54. package/dist/storefront/storefront.spec.js +1 -1
  55. package/dist/storefront/summary.js +27 -24
  56. package/dist/storefront/summary.spec.js +44 -86
  57. package/dist/storefront/transactions.js +27 -0
  58. package/dist/style/base/theme.js +3 -3
  59. package/dist/style/components/methods.js +43 -42
  60. package/dist/style/utils/color-values.js +1 -3
  61. package/dist/style/views/confirmation.js +0 -4
  62. package/dist/style/views/method-selector.js +1 -1
  63. package/dist/style/views/modal.js +3 -1
  64. package/dist/style/views/summary.js +5 -1
  65. package/dist/utils/format-currency.js +4 -2
  66. package/dist/utils/has-valid-css-selector.js +1 -1
  67. package/dist/utils/process-property-as-dom-element.js +0 -2
  68. package/dist/views/__snapshots__/summary.spec.js.snap +103 -113
  69. package/dist/views/common/iframe/base-iframe.js +10 -2
  70. package/dist/views/common/iframe/modal-iframe.js +44 -3
  71. package/dist/views/confirmation.js +44 -20
  72. package/dist/views/method-selector/express-methods/apple-pay.js +92 -0
  73. package/dist/views/method-selector/express-methods/google-pay.js +31 -0
  74. package/dist/views/method-selector/express-methods/paypal.js +19 -0
  75. package/dist/views/method-selector/generate-digital-wallet.js +68 -0
  76. package/dist/views/method-selector/generate-digital-wallet.spec.js +135 -0
  77. package/dist/views/method-selector/get-payment-methods.js +28 -8
  78. package/dist/views/method-selector/get-payment-methods.spec.js +25 -26
  79. package/dist/views/method-selector/index.js +55 -86
  80. package/dist/views/method-selector/method-selector.spec.js +80 -69
  81. package/dist/views/method-selector/mount-express-methods.js +38 -62
  82. package/dist/views/method-selector/mount-methods.js +18 -18
  83. package/dist/views/modal.js +21 -15
  84. package/dist/views/result.js +13 -16
  85. package/dist/views/summary.js +170 -114
  86. package/dist/views/summary.spec.js +72 -76
  87. package/package.json +5 -4
  88. package/src/events/base-event.js +15 -17
  89. package/src/events/events.spec.js +6 -4
  90. package/src/events/index.js +6 -3
  91. package/src/functions/destroy.js +12 -13
  92. package/src/functions/destroy.spec.js +30 -31
  93. package/src/functions/mount/fetch-data.js +148 -0
  94. package/src/functions/mount/fetch-data.spec.js +238 -0
  95. package/src/functions/mount/index.js +129 -244
  96. package/src/functions/mount/mount.spec.js +35 -139
  97. package/src/functions/mount/setup-element.js +26 -0
  98. package/src/functions/mount/setup-framepay.js +41 -0
  99. package/src/functions/mount/setup-i18n.js +19 -0
  100. package/src/functions/mount/setup-options.js +100 -0
  101. package/src/functions/mount/setup-options.spec.js +60 -0
  102. package/src/functions/mount/setup-storefront.js +24 -0
  103. package/src/functions/mount/setup-styles.js +30 -0
  104. package/src/functions/on.js +13 -8
  105. package/src/functions/on.spec.js +30 -17
  106. package/src/functions/purchase.js +101 -19
  107. package/src/functions/purchase.spec.js +18 -18
  108. package/src/functions/setup.js +48 -0
  109. package/src/functions/setup.spec.js +98 -0
  110. package/src/functions/show.js +20 -10
  111. package/src/functions/show.spec.js +43 -22
  112. package/src/functions/update.js +50 -27
  113. package/src/functions/update.spec.js +57 -22
  114. package/src/i18n/en.json +4 -1
  115. package/src/i18n/es.json +4 -1
  116. package/src/i18n/i18n.spec.js +6 -4
  117. package/src/i18n/index.js +14 -11
  118. package/src/index.js +41 -52
  119. package/src/index.spec.js +8 -37
  120. package/src/loader/index.js +51 -47
  121. package/src/loader/loader.spec.js +26 -19
  122. package/src/storefront/index.js +37 -7
  123. package/src/storefront/invoices.js +11 -0
  124. package/src/storefront/models/base-model.js +10 -0
  125. package/src/storefront/models/invoice-model.js +3 -0
  126. package/src/storefront/models/plan-model.js +3 -35
  127. package/src/storefront/models/product-model.js +3 -23
  128. package/src/storefront/models/ready-to-pay-model.js +3 -3
  129. package/src/storefront/models/summary-model.js +15 -29
  130. package/src/storefront/models/transaction-model.js +19 -0
  131. package/src/storefront/payment-instruments.js +30 -0
  132. package/src/storefront/payment-instruments.spec.js +69 -0
  133. package/src/storefront/plans.js +16 -23
  134. package/src/storefront/plans.spec.js +25 -54
  135. package/src/storefront/products.js +18 -22
  136. package/src/storefront/products.spec.js +23 -54
  137. package/src/storefront/purchase.js +14 -14
  138. package/src/storefront/purchase.spec.js +17 -29
  139. package/src/storefront/ready-to-pay.js +26 -23
  140. package/src/storefront/ready-to-pay.spec.js +41 -71
  141. package/src/storefront/storefront.spec.js +1 -1
  142. package/src/storefront/summary.js +26 -22
  143. package/src/storefront/summary.spec.js +60 -109
  144. package/src/storefront/transactions.js +11 -0
  145. package/src/style/base/theme.js +10 -8
  146. package/src/style/base/theme.spec.js +4 -2
  147. package/src/style/browserslist.js +1 -3
  148. package/src/style/components/button.js +3 -1
  149. package/src/style/components/forms/checkbox.js +3 -1
  150. package/src/style/components/index.js +1 -1
  151. package/src/style/components/loader.js +3 -1
  152. package/src/style/components/methods.js +43 -42
  153. package/src/style/helpers/index.js +1 -1
  154. package/src/style/index.js +2 -1
  155. package/src/style/utils/color-values.js +4 -4
  156. package/src/style/vendor/framepay.js +1 -1
  157. package/src/style/vendor/postmate.js +1 -1
  158. package/src/style/views/confirmation.js +0 -4
  159. package/src/style/views/index.js +1 -1
  160. package/src/style/views/method-selector.js +1 -1
  161. package/src/style/views/modal.js +4 -2
  162. package/src/style/views/summary.js +5 -1
  163. package/src/utils/add-dom-element.js +12 -13
  164. package/src/utils/format-currency.js +6 -2
  165. package/src/utils/has-valid-css-selector.js +2 -2
  166. package/src/utils/is-dom-element.js +1 -1
  167. package/src/utils/process-property-as-dom-element.js +27 -24
  168. package/src/utils/sleep.js +1 -1
  169. package/src/views/__snapshots__/summary.spec.js.snap +103 -113
  170. package/src/views/common/iframe/base-iframe.js +12 -4
  171. package/src/views/common/iframe/event-listeners.js +6 -6
  172. package/src/views/common/iframe/index.js +1 -1
  173. package/src/views/common/iframe/method-iframe.js +3 -6
  174. package/src/views/common/iframe/modal-iframe.js +42 -6
  175. package/src/views/common/iframe/view-iframe.js +3 -5
  176. package/src/views/common/render-utilities.js +3 -3
  177. package/src/views/confirmation.js +34 -25
  178. package/src/views/method-selector/express-methods/apple-pay.js +78 -0
  179. package/src/views/method-selector/express-methods/google-pay.js +24 -0
  180. package/src/views/method-selector/express-methods/paypal.js +7 -0
  181. package/src/views/method-selector/generate-digital-wallet.js +51 -0
  182. package/src/views/method-selector/generate-digital-wallet.spec.js +135 -0
  183. package/src/views/method-selector/get-method-data.js +7 -4
  184. package/src/views/method-selector/get-payment-methods.js +38 -29
  185. package/src/views/method-selector/get-payment-methods.spec.js +26 -33
  186. package/src/views/method-selector/index.js +70 -99
  187. package/src/views/method-selector/method-selector.spec.js +88 -78
  188. package/src/views/method-selector/mount-express-methods.js +36 -60
  189. package/src/views/method-selector/mount-methods.js +32 -21
  190. package/src/views/modal.js +37 -23
  191. package/src/views/result.js +12 -15
  192. package/src/views/summary.js +169 -101
  193. package/src/views/summary.spec.js +99 -74
  194. package/tests/async-utilities.js +22 -0
  195. package/tests/mocks/rebilly-instruments-mock.js +89 -77
  196. package/tests/mocks/storefront-api-mock.js +8 -0
  197. package/tests/mocks/storefront-mock.js +17 -0
  198. package/dist/events/purchase-completed.js +0 -24
  199. package/dist/functions/initialize.js +0 -82
  200. package/dist/functions/initialize.spec.js +0 -34
  201. package/dist/functions/mount/fetch-summary-data.js +0 -31
  202. package/dist/functions/mount/fetch-summary-data.spec.js +0 -45
  203. package/dist/views/method-selector/process-digital-wallet-options.js +0 -35
  204. package/dist/views/method-selector/process-digital-wallet-options.spec.js +0 -80
  205. package/src/events/purchase-completed.js +0 -11
  206. package/src/functions/initialize.js +0 -74
  207. package/src/functions/initialize.spec.js +0 -38
  208. package/src/functions/mount/fetch-summary-data.js +0 -26
  209. package/src/functions/mount/fetch-summary-data.spec.js +0 -46
  210. package/src/views/method-selector/process-digital-wallet-options.js +0 -16
  211. package/src/views/method-selector/process-digital-wallet-options.spec.js +0 -94
package/src/index.spec.js CHANGED
@@ -1,14 +1,9 @@
1
- import RebillyInstruments, {RebillyInstrumentsInstance} from './index';
1
+ import RebillyInstruments, { RebillyInstrumentsInstance } from './index';
2
+ import { avoidUnhandledPromises } from 'tests/async-utilities';
2
3
 
3
4
  describe('RebillyInstruments instance', () => {
4
5
  let rebillyInstruments;
5
6
 
6
- function setupInstance(config = {
7
- test: 'value'
8
- }) {
9
- rebillyInstruments.initialize(config);
10
- }
11
-
12
7
  beforeEach(() => {
13
8
  rebillyInstruments = RebillyInstruments;
14
9
  });
@@ -17,48 +12,24 @@ describe('RebillyInstruments instance', () => {
17
12
  expect(RebillyInstruments).toBeInstanceOf(RebillyInstrumentsInstance);
18
13
  });
19
14
 
20
- it('should be initializable', () => {
21
- jest.spyOn(rebillyInstruments, 'initialize');
22
-
23
- const config = {
24
- test: 'value'
25
- };
26
- rebillyInstruments.initialize(config);
27
-
28
- expect(rebillyInstruments.initialize).toHaveBeenCalledTimes(1);
29
- expect(rebillyInstruments.initialize).toHaveBeenCalledWith(config);
30
- });
31
-
32
- it('should be mountable', () => {
15
+ it('should be mountable', async () => {
33
16
  jest.spyOn(rebillyInstruments, 'mount');
34
17
 
35
- document.body.innerHTML =
36
- `
18
+ document.body.innerHTML = `
37
19
  <div class="form-selector"></div>
38
20
  <div class="summary-selector"></div>
39
21
  `;
40
22
 
41
23
  const options = {
42
24
  form: '.form-selector',
43
- test: 'value'
25
+ summary: '.summary-selector',
26
+ items: []
44
27
  };
45
28
 
46
- setupInstance();
47
- rebillyInstruments.mount(options);
29
+ await rebillyInstruments.mount(options);
48
30
 
49
31
  expect(rebillyInstruments.mount).toHaveBeenCalledTimes(1);
50
32
  expect(rebillyInstruments.mount).toHaveBeenCalledWith(options);
51
- });
52
-
53
- it('should be able to trigger a purchase', () => {
54
- jest.spyOn(rebillyInstruments, 'purchase');
55
-
56
- const instrument = {};
57
-
58
- setupInstance();
59
- rebillyInstruments.purchase(instrument);
60
-
61
- expect(rebillyInstruments.purchase).toHaveBeenCalledTimes(1);
33
+ await avoidUnhandledPromises();
62
34
  });
63
35
  });
64
-
@@ -1,66 +1,70 @@
1
1
  import isDOMElement from '../utils/is-dom-element';
2
2
 
3
3
  export class Loader {
4
- constructor() {
5
- this.summary = [];
6
- this.form = [];
7
- this.modal = [];
8
- this.DOM = {
9
- loading: `
4
+ constructor() {
5
+ this.summary = [];
6
+ this.form = [];
7
+ this.modal = [];
8
+ this.DOM = {
9
+ loading: `
10
10
  <div class="rebilly-instruments-loader is-active">
11
11
  <div class="rebilly-instruments-loader-spinner"></div>
12
12
  </div>
13
13
  `
14
- };
15
- }
16
-
17
- addDOMElement({section = 'form', el = null} = {}) {
18
- const minHeight = '200px';
14
+ };
15
+ }
19
16
 
20
- if (isDOMElement(el)) {
21
- el.style.position = 'relative';
22
- el.style.minHeight = minHeight;
17
+ addDOMElement({ section = 'form', el = null } = {}) {
18
+ if (isDOMElement(el)) {
19
+ el.style.position = 'relative';
23
20
 
24
- this.DOM[section] = el;
25
- }
21
+ this.DOM[section] = el;
26
22
  }
23
+ }
27
24
 
28
- startLoading({section = 'form', id = ''} = {}) {
29
- this[section].push(id);
25
+ startLoading({ section = 'form', id = '' } = {}) {
26
+ const minHeight = '200px';
27
+ this[section].push(id);
30
28
 
31
- if (this.DOM?.[section]) {
32
- if (!this.DOM[section].querySelector('.rebilly-instruments-loader')) {
33
- this.DOM[section].innerHTML = this.DOM.loading;
34
- } else {
35
- this.DOM[section].querySelector('.rebilly-instruments-loader')
36
- .classList.add('is-active');
37
- }
38
- }
29
+ if (this.DOM?.[section]) {
30
+ if (!this.DOM[section].querySelector('.rebilly-instruments-loader')) {
31
+ this.DOM[section].innerHTML = this.DOM.loading;
32
+ } else {
33
+ this.DOM[section]
34
+ .querySelector('.rebilly-instruments-loader')
35
+ .classList.add('is-active');
36
+ }
37
+ this.DOM[section].style.minHeight = minHeight;
39
38
  }
39
+ }
40
40
 
41
- stopLoading({section = 'form', id = ''} = {}) {
42
- const idIndex = this[section].indexOf(id);
41
+ stopLoading({ section = 'form', id = '' } = {}) {
42
+ const idIndex = this[section].indexOf(id);
43
43
 
44
- if (idIndex !== -1) {
45
- this[section].splice(idIndex, 1);
46
- }
47
-
48
- if (!this[section].length
49
- && this.DOM[section].querySelector('.rebilly-instruments-loader')) {
50
- this.DOM[section].querySelector('.rebilly-instruments-loader')
51
- .classList.remove('is-active');
52
- }
44
+ if (idIndex !== -1) {
45
+ this[section].splice(idIndex, 1);
53
46
  }
54
47
 
55
- clearAll() {
56
- this.form.forEach(id => {
57
- this.stopLoading({id})
58
- });
59
- this.summary.forEach(id => {
60
- this.stopLoading({section: 'summary', id})
61
- });
62
- this.modal.forEach(id => {
63
- this.stopLoading({section: 'modal', id})
64
- });
48
+ if (
49
+ !this[section].length &&
50
+ this.DOM[section].querySelector('.rebilly-instruments-loader')
51
+ ) {
52
+ this.DOM[section]
53
+ .querySelector('.rebilly-instruments-loader')
54
+ .classList.remove('is-active');
55
+ this.DOM[section].style.minHeight = '';
65
56
  }
57
+ }
58
+
59
+ clearAll() {
60
+ this.form.forEach((id) => {
61
+ this.stopLoading({ id });
62
+ });
63
+ this.summary.forEach((id) => {
64
+ this.stopLoading({ section: 'summary', id });
65
+ });
66
+ this.modal.forEach((id) => {
67
+ this.stopLoading({ section: 'modal', id });
68
+ });
69
+ }
66
70
  }
@@ -1,5 +1,5 @@
1
1
  import { Loader } from './index';
2
-
2
+
3
3
  describe('Loader Class', () => {
4
4
  let loader;
5
5
 
@@ -11,56 +11,63 @@ describe('Loader Class', () => {
11
11
  <div class="summary-selector"></div>
12
12
  `;
13
13
 
14
- loader.addDOMElement({el: document.querySelector('.form-selector')});
15
- loader.addDOMElement({section: 'summary', el: document.querySelector('.summary-selector')});
16
- })
14
+ loader.addDOMElement({ el: document.querySelector('.form-selector') });
15
+ loader.addDOMElement({
16
+ section: 'summary',
17
+ el: document.querySelector('.summary-selector')
18
+ });
19
+ });
17
20
 
18
21
  it('manage state of loading ids', () => {
19
- loader.startLoading({id: 'loadForm'});
20
- loader.startLoading({id: 'updateForm'});
22
+ loader.startLoading({ id: 'loadForm' });
23
+ loader.startLoading({ id: 'updateForm' });
21
24
 
22
25
  // Start loading state
23
- loader.startLoading({section: 'summary', id: 'loadSummary'});
24
- loader.startLoading({section: 'summary', id: 'updateSummary'});
26
+ loader.startLoading({ section: 'summary', id: 'loadSummary' });
27
+ loader.startLoading({ section: 'summary', id: 'updateSummary' });
25
28
 
26
29
  // Stop one loading state
27
- loader.stopLoading({id: 'loadForm'});
30
+ loader.stopLoading({ id: 'loadForm' });
28
31
 
29
32
  expect(loader.form.length).toEqual(1);
30
33
  expect(loader.summary.length).toEqual(2);
31
34
  });
32
35
 
33
36
  it('adds the loader HTML on element and removes the active class when stoped', () => {
34
- loader.startLoading({id: 'loadForm'});
37
+ loader.startLoading({ id: 'loadForm' });
35
38
 
36
39
  expect(loader.DOM.form.innerHTML).toMatch('rebilly-instruments-loader');
37
40
  expect(
38
- loader.DOM.form.querySelector('.rebilly-instruments-loader')
41
+ loader.DOM.form
42
+ .querySelector('.rebilly-instruments-loader')
39
43
  .classList.contains('is-active')
40
44
  ).toEqual(true);
41
45
 
42
- loader.stopLoading({id: 'loadForm'});
46
+ loader.stopLoading({ id: 'loadForm' });
43
47
 
44
48
  expect(
45
- loader.DOM.form.querySelector('.rebilly-instruments-loader')
49
+ loader.DOM.form
50
+ .querySelector('.rebilly-instruments-loader')
46
51
  .classList.contains('is-active')
47
52
  ).toEqual(false);
48
53
  });
49
54
 
50
55
  it('keeps loading when there are Ids on the element array', () => {
51
- loader.startLoading({id: 'loadForm'});
52
- loader.startLoading({id: 'updateForm'});
56
+ loader.startLoading({ id: 'loadForm' });
57
+ loader.startLoading({ id: 'updateForm' });
53
58
 
54
59
  expect(loader.DOM.form.innerHTML).toMatch('rebilly-instruments-loader');
55
60
  expect(
56
- loader.DOM.form.querySelector('.rebilly-instruments-loader')
61
+ loader.DOM.form
62
+ .querySelector('.rebilly-instruments-loader')
57
63
  .classList.contains('is-active')
58
64
  ).toEqual(true);
59
65
 
60
- loader.stopLoading({id: 'loadForm'});
61
-
66
+ loader.stopLoading({ id: 'loadForm' });
67
+
62
68
  expect(
63
- loader.DOM.form.querySelector('.rebilly-instruments-loader')
69
+ loader.DOM.form
70
+ .querySelector('.rebilly-instruments-loader')
64
71
  .classList.contains('is-active')
65
72
  ).toEqual(true);
66
73
  });
@@ -1,7 +1,35 @@
1
- import {RebillyStorefrontAPI} from 'rebilly-js-sdk';
1
+ import { RebillyStorefrontAPI } from 'rebilly-js-sdk';
2
+
3
+ export function validateStateForStorefront({state}) {
4
+ if (!state.storefront) {
5
+ throw new Error('Could not access rebilly-js-sdk instance');
6
+ }
7
+
8
+ if (!state.options) {
9
+ throw new Error(
10
+ 'Could not use Rebilly Instruments mount options to fetch Rebilly data'
11
+ );
12
+ }
13
+ }
14
+
15
+ export async function Endpoint({
16
+ state
17
+ } = {}, callback) {
18
+ try {
19
+ validateStateForStorefront({state});
20
+ return await callback();
21
+ } catch(error) {
22
+ // TODO: standard error handling for all endpoints
23
+ // console.error(error);
24
+ if(error.status === 422) {
25
+ error.details.forEach(e => console.error(e));
26
+ }
27
+ throw error;
28
+ }
29
+ }
2
30
 
3
31
  export class StorefrontInstance {
4
- constructor ({
32
+ constructor({
5
33
  publishableKey = null,
6
34
  organizationId = null,
7
35
  mode = 'live',
@@ -17,15 +45,17 @@ export class StorefrontInstance {
17
45
  const api = RebillyStorefrontAPI({
18
46
  organizationId,
19
47
  sandbox: mode === 'sandbox',
20
- timeout: Number.isNaN(parseInt(timeout, 10)) ? 10000 : parseInt(timeout, 10),
21
- urls,
48
+ timeout: Number.isNaN(parseInt(timeout, 10))
49
+ ? 10000
50
+ : parseInt(timeout, 10),
51
+ urls
22
52
  });
23
-
53
+
24
54
  api.setPublishableKey(publishableKey);
25
-
55
+
26
56
  this.api = api;
27
57
  return this.api;
28
58
  }
29
59
  }
30
60
 
31
- export default (config = {}) => new StorefrontInstance(config)
61
+ export default (config = {}) => new StorefrontInstance(config);
@@ -0,0 +1,11 @@
1
+ import InvoiceModel from './models/invoice-model';
2
+ import { Endpoint } from './index';
3
+
4
+ export async function fetchInvoice({ data = null, state = null }) {
5
+ return Endpoint({state}, async () => {
6
+ state.storefront.setSessionToken(state.options.customerJwt);
7
+ const {fields} = await state.storefront.invoices.get(data);
8
+
9
+ return new InvoiceModel(fields);
10
+ });
11
+ }
@@ -0,0 +1,10 @@
1
+ export default class BaseModel {
2
+ constructor({
3
+ ...fields
4
+ } = {}) {
5
+ Object.entries(fields)
6
+ .forEach(([key, value]) => {
7
+ this[key] = value;
8
+ })
9
+ }
10
+ }
@@ -0,0 +1,3 @@
1
+ import BaseModel from './base-model';
2
+
3
+ export default class InvoiceModel extends BaseModel {}
@@ -1,35 +1,3 @@
1
- export default class PlanModel {
2
- constructor({
3
- id = '',
4
- name = '',
5
- productId = '',
6
- productOptions = {},
7
- currency = '',
8
- currencySign = '',
9
- pricing = {},
10
- recurringInterval = {},
11
- trial = null,
12
- isTrialOnly = false,
13
- setup = null,
14
- customFields = {},
15
- createdTime = '',
16
- updatedTime = '',
17
- invoiceTimeShift = {},
18
- } = {}) {
19
- this.id = id;
20
- this.name = name;
21
- this.productId = productId;
22
- this.productOptions = productOptions;
23
- this.currency = currency;
24
- this.currencySign = currencySign;
25
- this.pricing = pricing;
26
- this.recurringInterval = recurringInterval;
27
- this.trial = trial;
28
- this.isTrialOnly = isTrialOnly;
29
- this.setup = setup;
30
- this.customFields = customFields;
31
- this.createdTime = createdTime;
32
- this.updatedTime = updatedTime;
33
- this.invoiceTimeShift = invoiceTimeShift;
34
- }
35
- }
1
+ import BaseModel from './base-model';
2
+
3
+ export default class PlanModel extends BaseModel {}
@@ -1,23 +1,3 @@
1
- export default class ProductModel {
2
- constructor({
3
- id = '',
4
- name = '',
5
- unitLabel = '',
6
- description = {},
7
- requiresShipping = false,
8
- options = [],
9
- customFields = {},
10
- createdTime = '',
11
- updatedTime = '',
12
- } = {}) {
13
- this.id = id;
14
- this.name = name;
15
- this.unitLabel = unitLabel;
16
- this.description = description;
17
- this.requiresShipping = requiresShipping;
18
- this.options = options;
19
- this.customFields = customFields;
20
- this.createdTime = createdTime;
21
- this.updatedTime = updatedTime;
22
- }
23
- }
1
+ import BaseModel from './base-model';
2
+
3
+ export default class ProductModel extends BaseModel {}
@@ -6,7 +6,7 @@ export class ReadyToPayFeatureModel {
6
6
  merchantOrigin = '',
7
7
  // Plaid fields
8
8
  linkToken = '',
9
- expirationTime = '',
9
+ expirationTime = ''
10
10
  } = {}) {
11
11
  this.name = name;
12
12
  this.merchantName = merchantName;
@@ -22,7 +22,7 @@ export default class ReadyToPayModel {
22
22
  method = '',
23
23
  feature = null,
24
24
  brands = [],
25
- filters = [],
25
+ filters = []
26
26
  } = {}) {
27
27
  this.index = index;
28
28
  this.method = method;
@@ -30,4 +30,4 @@ export default class ReadyToPayModel {
30
30
  this.brands = brands;
31
31
  this.filters = filters;
32
32
  }
33
- }
33
+ }
@@ -1,42 +1,28 @@
1
- export class SummaryLineItemModel{
1
+ import BaseModel from './base-model';
2
+
3
+ export class SummaryLineItemModel extends BaseModel {
2
4
  constructor({
3
- type = '',
4
- description = '',
5
5
  unitPrice = 0.0,
6
6
  quantity = 0,
7
7
  price = 0.0,
8
- planId = '',
9
- productId = ''
8
+ ...fields
10
9
  } = {}) {
11
- this.type = type;
12
- this.description = description;
10
+ super(fields);
13
11
  this.unitPrice = unitPrice;
14
12
  this.quantity = quantity;
15
13
  this.price = price;
16
- this.planId = planId;
17
- this.productId = productId;
18
14
  }
19
15
  }
20
16
 
21
- export class SummaryTaxModel {
22
- constructor({
23
- amount = '',
24
- description = ''
25
- } = {}) {
26
- this.amount = amount;
27
- this.description = description;
28
- }
29
- }
17
+ export class SummaryTaxModel extends BaseModel {}
30
18
 
31
- export class SummaryDiscountModel {
19
+ export class SummaryDiscountModel extends BaseModel {
32
20
  constructor({
33
- couponId = '',
34
21
  amount = 0,
35
- description = ''
36
- } = {}) {
37
- this.couponId = couponId;
22
+ ...fields
23
+ }) {
24
+ super(fields);
38
25
  this.amount = amount;
39
- this.description = description;
40
26
  }
41
27
  }
42
28
 
@@ -53,13 +39,13 @@ export default class SummaryModel {
53
39
  total = 0.0
54
40
  } = {}) {
55
41
  function map(array) {
56
- const list = (Array.isArray(array) ? array : []);
42
+ const list = Array.isArray(array) ? array : [];
57
43
  return {
58
44
  // eslint-disable-next-line new-cap
59
- to: (instance) => list.map(item => new instance(item))
60
- }
45
+ to: (instance) => list.map((item) => new instance(item))
46
+ };
61
47
  }
62
-
48
+
63
49
  this.currency = currency;
64
50
  this.lineItems = map(lineItems).to(SummaryLineItemModel);
65
51
  this.taxes = map(taxes).to(SummaryTaxModel);
@@ -70,4 +56,4 @@ export default class SummaryModel {
70
56
  this.discountsAmount = discountsAmount;
71
57
  this.total = total;
72
58
  }
73
- }
59
+ }
@@ -0,0 +1,19 @@
1
+ import BaseModel from './base-model';
2
+
3
+ export default class TransactionModel extends BaseModel {
4
+ constructor({
5
+ invoiceIds = [],
6
+ ...fields
7
+ }) {
8
+ super(fields);
9
+ this.invoiceIds = invoiceIds || [];
10
+ }
11
+
12
+ get invoiceId () {
13
+ return this.invoiceIds[0] || null;
14
+ }
15
+
16
+ get hasInvoice () {
17
+ return !!this.invoiceId;
18
+ }
19
+ }
@@ -0,0 +1,30 @@
1
+ import { Endpoint } from './index';
2
+
3
+ export async function setupPaymentInstrument({ data, state }) {
4
+ return Endpoint({state}, async () => {
5
+ // Create payment instrument
6
+ const { fields: instrument } = await state.storefront.paymentInstruments.create({data});
7
+
8
+ // Setup the payment instrument
9
+ state.storefront.setSessionToken(instrument.token);
10
+
11
+ const setupPayload = {
12
+ id: instrument.id,
13
+ data: {
14
+ websiteId: state.options.websiteId,
15
+ ...data
16
+ }
17
+ }
18
+
19
+ if(state.data.getAmountAndCurrency) {
20
+ setupPayload.data = {
21
+ ...setupPayload.data,
22
+ ...state.data.getAmountAndCurrency
23
+ }
24
+ }
25
+
26
+ const { fields: transaction } = await state.storefront.paymentInstruments.setup(setupPayload);
27
+
28
+ return {instrument, transaction};
29
+ });
30
+ }
@@ -0,0 +1,69 @@
1
+ import { StorefontTestingInstance } from 'tests/mocks/storefront-mock';
2
+ import { ok, post } from 'msw-when-then';
3
+ import { when } from 'tests/msw/server';
4
+ import { storefrontURL } from 'tests/mocks/storefront-api-mock';
5
+
6
+ describe('Storefront API Setup', () => {
7
+ it('can make purchase', async () => {
8
+ const instance = new StorefontTestingInstance();
9
+
10
+ const paymentInstrumentFields = {
11
+ id: 'payment-instrument-id'
12
+ };
13
+
14
+ const paymentInstrumentSetupFields = {
15
+ id: 'payment-instrument-id',
16
+ approvalUrl: null
17
+ };
18
+
19
+ const paymentInstrumentPayload = {
20
+ data: {
21
+ token: 'test-token'
22
+ }
23
+ };
24
+
25
+ const paymentInstrumentSetupPayload = {
26
+ id: paymentInstrumentFields.id,
27
+ data: {
28
+ websiteId: 'website-id',
29
+ currency: 'USD',
30
+ amount: 30
31
+ }
32
+ };
33
+
34
+ when(post(`${storefrontURL}/payment-instruments`)).thenReturn(ok(paymentInstrumentFields));
35
+ when(post(`${storefrontURL}/payment-instruments/*/setup`)).thenReturn(ok(paymentInstrumentSetupFields));
36
+
37
+
38
+ jest.spyOn(instance.storefront.paymentInstruments, 'create');
39
+ jest.spyOn(instance.storefront.paymentInstruments, 'setup');
40
+
41
+ const paymentInstrumentResponse = await instance.storefront.paymentInstruments.create(
42
+ paymentInstrumentPayload
43
+ );
44
+
45
+ const paymentInstrumentSetupResponse = await instance.storefront.paymentInstruments.setup(
46
+ paymentInstrumentSetupPayload
47
+ );
48
+
49
+ expect(instance.storefront.paymentInstruments.create).toBeCalledTimes(1);
50
+ expect(instance.storefront.paymentInstruments.create).toBeCalledWith(paymentInstrumentPayload);
51
+
52
+ expect(instance.storefront.paymentInstruments.setup).toBeCalledTimes(1);
53
+ expect(instance.storefront.paymentInstruments.setup).toBeCalledWith(paymentInstrumentSetupPayload);
54
+
55
+ expect(paymentInstrumentResponse.config).toBeInstanceOf(Object);
56
+ expect(paymentInstrumentResponse.fields).toBeInstanceOf(Object);
57
+ expect(paymentInstrumentResponse.response).toBeInstanceOf(Object);
58
+
59
+ expect(paymentInstrumentResponse.fields).toEqual(paymentInstrumentFields);
60
+ expect(paymentInstrumentResponse.fields).toMatchObject(paymentInstrumentFields);
61
+
62
+ expect(paymentInstrumentSetupResponse.config).toBeInstanceOf(Object);
63
+ expect(paymentInstrumentSetupResponse.fields).toBeInstanceOf(Object);
64
+ expect(paymentInstrumentSetupResponse.response).toBeInstanceOf(Object);
65
+
66
+ expect(paymentInstrumentSetupResponse.fields).toEqual(paymentInstrumentSetupFields);
67
+ expect(paymentInstrumentSetupResponse.fields).toMatchObject(paymentInstrumentSetupFields);
68
+ });
69
+ });