@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
@@ -1,271 +1,156 @@
1
1
  /* eslint-disable max-len */
2
- import { collectData } from '@rebilly/risk-data-collector';
3
- import merge from 'lodash.merge';
4
- import { mainStyle } from '../../style';
5
- import { addDOMElement, processPropertyAsDOMElement } from '../../utils';
6
- import { FetchSummaryData } from './fetch-summary-data';
2
+ import { mountSummary } from '../../views/summary';
3
+ import { mountMethodSelector } from '../../views/method-selector';
4
+ import { fetchData } from './fetch-data';
5
+ import { show } from '../show';
6
+ import { on } from '../on';
7
+ import setupElement from './setup-element';
8
+ import setupStorefront from './setup-storefront';
9
+ import setupOptions from './setup-options';
10
+ import setupFramepay from './setup-framepay';
11
+ import setupStyles from './setup-styles';
12
+ import setupI18n from './setup-i18n';
7
13
 
8
14
  /**
9
15
  * @typedef {object} Item
10
16
  * @property {string} planId - The Rebilly id of the plan.
11
17
  * @property {number} quantity - The number of the plans to be purchased.
12
18
  * @property {string} thumbnail - The source img for the product. Recommend 100px by 100px.
13
- */
14
-
15
- /**
16
- * @typedef {object} Intent
17
- * @property {"purchase" | "vault"} mode - Which mode the mount is usings. Default "purchase".
18
- * @property {Array.<Item>} items - Which plans the customer is purchasing.
19
- * @property {string} customerId - Which customer is associated with the instrument.
20
- * @property {string} countryCode - The country code for the transaction
21
- */
22
-
23
- /**
24
- * @typedef {GooglePayDisplayOptions} GooglePay
25
- * @param {"back" | "white"} buttonColor - default "black". Color of google pay button
26
- * @param {"short" | "long"} buttonType - default "short". The length of the button
27
- * @param {string} buttonHeight - The value and units of the button to match other payment buttons
28
- * <br>example: "44px", "1rem" etc.
29
- */
30
-
31
- /**
32
- * @typedef {object} GooglePay
33
- * @param {GooglePayDisplayOptions} displayOptions - display options for google pay instrument
34
- */
35
-
36
- /**
37
- * @typedef {object} PaymentCard
38
- * @param {boolean} popup - default: false. Show method as a button with a form popup
39
- * <br>Otherwise the form will be mounted inline.
40
- */
41
-
42
- /**
19
+ *
20
+ * @typedef {object} GooglePayDisplayOptions
21
+ * @property {"back" | "white"} [buttonColor=black] - Color of google pay button
22
+ * @property {"short" | "long"} [buttonType=short] - The length of the button
23
+ * @property {string} [buttonHeight=44px] - The value and units of the button to match other payment buttons
24
+ *
25
+ * @typedef {object} ApplePayDisplayOptions
26
+ * @property {"back" | "white"} [buttonColor=black] - Color of apple pay button
27
+ * @property {"short" | "long"} [buttonType=short] - The length of the button
28
+ * @property {string} [buttonHeight=44px] - The value and units of the button to match other payment buttons
29
+ *
30
+ * @typedef {object} ApplePayMerchantOptions
31
+ * @property {string} merchantName - The name of the merchant store.
32
+ *
33
+ * @typedef {object} GooglePay
34
+ * @property {GooglePayDisplayOptions} displayOptions - display options for google pay instrument
35
+ *
36
+ * @typedef {object} ApplePay
37
+ * @property {ApplePayDisplayOptions} displayOptions - display options for apple pay instrument
38
+ * @property {ApplePayMerchantOptions} merchantOptions - merchant options for apple pay instrument
39
+ *
40
+ * @typedef {object} PaymentCard
41
+ * @property {boolean} [popup=true] - Show method as a button with a form popup. Otherwise the form will be mounted inline.
42
+ *
43
43
  * @typedef {object} Address
44
- * @param {string} name - default: default. One of default, combined, or stacked.
45
- * @param {string} region - default: default. One of default, split, or stacked.
46
- * @param {Array.<"organization" | "phoneNumber">} show. Show extra fields listed.
47
- * @param {Array.<"address" | "address2" | "email | "country" | "region" | "postalCode"">} hide. Hide the listed fields.
48
- * @param {Array.<"organization" | "address" | "address2" | "email" | "phoneNumber" | "country" | "region" | "postalCode">} require.
49
- * <br>If the field name is included, enforce the data for those inputs to be included.
50
- * <br>First name and last name are always required. Country is always required if the products require shipping.
51
- */
52
-
53
- /**
44
+ * @property {string} [name=default] - One of default, combined, or stacked.
45
+ * @property {string} [region=default] - One of default, split, or stacked.
46
+ * @property {Array.<"organization" | "phoneNumber">} show - Show extra fields listed.
47
+ * @property {Array.<"address" | "address2" | "email | "country" | "region" | "postalCode"">} hide - Hide the listed fields.
48
+ * @property {Array.<"organization" | "address" | "address2" | "email" | "phoneNumber" | "country" | "region" | "postalCode">} require - Require the listed fields.
49
+ *
54
50
  * @typedef {object} PaymentInstruments
55
- * @param {boolean} compactExpressInstruments - default: true. Show express methods as
56
- * <br>inline pill buttons, or list of full width button.
57
- * @param {PaymentCard} paymentCard - settings for payment card instruments
58
- * @param {GooglePay} googlePay - settings for google pay instruments
59
- * @param {Address} address - customization for address components for all payment instruments.
60
- */
61
-
62
- /**
63
- * @typedef {object} Features
64
- * @param {boolean} autoConfirmation - default: true. Will mount the confirmation screen after `instrument-ready`
65
- * <br>event is triggered. Will need to trigger purchase manually if set to false.
66
- * <br>Can use RebillyInstruments.show('confirmation', options) to mount defautl component
67
- * @param {boolean} autoResult - default: true. Show results of transaction after `purchase-completed` event is triggered
68
- * <br>Will need to handle purchase result manually if set to false.
69
- * <br>Can use RebillyInstruments.show('result', options) to display default component
70
- */
71
-
72
- /**
73
- * @typedef {object} Options
74
- * @property {Intent} intent - The information required for purchaseing or vaulting an instrument
75
- * @property {PaymentInstruments} paymentInstruments - settings for various payment instruments
76
- * @property {Features} features - flags to enable and disable different features
77
- * @property {string} locale - default: auto. Language to render component text
51
+ * @property {boolean} [compactExpressInstruments=true] - Show express methods as inline pill buttons, or list of full width button.
52
+ * @property {PaymentCard} paymentCard - settings for payment card instruments
53
+ * @property {GooglePay} googlePay - settings for google pay instruments
54
+ * @property {ApplePay} applePay - settings for apple pay instruments
55
+ * @property {Address} address - customization for address components for all payment instruments.
56
+ *
57
+ * @typedef {object} Features
58
+ * @property {boolean} [autoConfirmation=true] - Will mount the confirmation screen after `instrument-ready` event is triggered.
59
+ * @property {boolean} [autoResult=true] - Show results of transaction after `purchase-completed` event is triggered
78
60
  */
79
61
 
80
62
  /**
81
63
  * Mount library with configurations.
82
- * @param {string | HTMLElement} form - The CSS class or HTML element were the form will be mounted.
83
- * @param {string | HTMLElement} summary - The CSS class or HTML element were the summary will be mounted.
84
- * @param {Options} options - The configurations that are to be passed to the library for use.
64
+ * @param {object} options - The options object
65
+ * @param {object} options.state - Global state
66
+ * @param {string | HTMLElement} options.form - The CSS class or HTML element were the form will be mounted.
67
+ * @param {string | HTMLElement} options.summary - The CSS class or HTML element were the summary will be mounted.
68
+ * @param {Item[]} options.items - Which plans the customer is purchasing.
69
+ * @param {string} options.invoiceId - The Rebilly id of the invoice used for purchasing.
70
+ * @param {string} options.customerJwt - The customer token to access the invoice.
71
+ * @param {string} [options.countryCode=USD] - The country code for the transaction
72
+ * @param {PaymentInstruments} options.paymentInstruments - settings for various payment instruments
73
+ * @param {Features} options.features - flags to enable and disable different features
74
+ * @param {string} options.locale - default: auto. Language to render component text
85
75
  */
86
- export async function Mount (
87
- {
88
- form = '.rebilly-instruments',
89
- summary = '.rebilly-instruments-summary',
90
- options = {},
91
- _dev = null,
92
- } = {},
93
- ) {
94
- this.form = form;
95
- this.summary = summary;
96
- this.mainStyle = null;
97
- this._dev = _dev;
98
-
99
- const framePayUrls = {
100
- script: _dev ? _dev.framePayScriptLink || 'https://framepay.rebilly.com/rebilly.js' : 'https://framepay.rebilly.com/rebilly.js',
101
- style: _dev ? _dev.framePayStyleLink || 'https://framepay.rebilly.com/rebilly.css' : 'https://framepay.rebilly.com/rebilly.css'
102
- };
103
- const _computed = {
104
- paymentMethodsUrl: _dev ? _dev.paymentMethodsUrl || 'https://forms.local.rebilly.dev:3000' : 'https://forms.secure-payments.app',
105
- }
106
-
107
- const OPTIONS_DEFAULTS = {
108
- intent: {
109
- countryCode: 'US'
110
- },
111
- locale: 'auto',
112
- paymentInstruments: {
113
- address: {
114
- name: 'default',
115
- region: 'default',
116
- hide: [],
117
- show: [],
118
- require: [],
119
- },
120
- compactExpressInstruments: true,
121
- googlePay: {
122
- displayOptions: {
123
- buttonColor: 'black',
124
- buttonType: 'short',
125
- buttonHeight: '44px'
126
- }
127
- },
128
- paymentCard: {
129
- popup: false
130
- }
131
- },
132
- features: {
133
- autoConfirmation: true,
134
- autoResult: true
135
- }
136
- };
137
-
138
- const combinedOptions = {...options, _computed};
139
- if (_dev) {
140
- combinedOptions._dev = _dev;
141
- }
142
-
143
- this.options = merge(OPTIONS_DEFAULTS, combinedOptions);
144
- this.form = processPropertyAsDOMElement({prop: this.form, propName: 'form'});
145
- this.summary = processPropertyAsDOMElement({prop: this.summary, propName: 'summary', isRequired: false});
146
-
147
- // Setup loader
148
- this.loader.addDOMElement({el: this.form});
149
- this.loader.addDOMElement({section: 'summary', el: this.summary});
150
-
151
- // Adds base stylesheet
152
- this.mainStyle = await mainStyle(this.configs?.theme || {});
153
- addDOMElement(
154
- {
155
- element: 'style',
156
- attributes: {type: 'text/css'},
157
- content: this.mainStyle,
158
- target: 'head'
159
- }
160
- );
161
-
162
- // Adds configs CSS to override any styles
163
- if (this.configs.css) {
164
- addDOMElement(
165
- {
166
- element: 'style',
167
- attributes: {type: 'text/css'},
168
- content: this.configs.css,
169
- target: 'head'
170
- }
171
- );
172
- }
173
-
174
- // Adds FramePay
175
- if (!document.querySelectorAll('[framepay*="script"]').length) {
176
- addDOMElement(
177
- {
178
- element: 'script',
179
- attributes: {
180
- 'framepay': 'script',
181
- src: framePayUrls.script
182
- },
183
- target: 'head'
184
- }
185
- );
186
- }
187
-
188
- if(!document.querySelectorAll('[framepay*="stylesheet"]').length) {
189
- addDOMElement(
190
- {
191
- element: 'link',
192
- attributes: {
193
- 'framepay': 'stylesheet',
194
- href: framePayUrls.style,
195
- rel: 'stylesheet'
196
- },
197
- target: 'head'
198
- }
199
- );
200
- }
201
-
76
+ export async function mount({
77
+ state,
78
+ ...options
79
+ } = {}) {
202
80
  try {
203
- this.loader.startLoading({section: 'summary', id: 'initSummary'});
204
- this.loader.startLoading({id: 'initForm'});
205
-
206
- const {riskMetadata} = await collectData();
207
-
208
- if (this.options.locale === 'auto' && riskMetadata?.browserData?.language) {
209
- const {browserData: {language}} = riskMetadata;
210
- this.options.locale = language;
81
+ state.data = {};
82
+ state.options = {};
83
+ // Setup DOM
84
+ state.form = setupElement({ element: 'form', options });
85
+ state.summary = setupElement({ element: 'summary', options });
86
+
87
+ // Setup loader
88
+ state.loader.addDOMElement({ el: state.form });
89
+ state.loader.addDOMElement({ section: 'summary', el: state.summary });
90
+ state.loader.startLoading({ section: 'summary', id: 'initSummary' });
91
+ state.loader.startLoading({ id: 'initForm' });
92
+
93
+ // Setup state
94
+ state.storefront = setupStorefront({ options });
95
+ state.options = setupOptions({ options });
96
+ state.mainStyle = await setupStyles({ options });
97
+ state.data = await fetchData({ state });
98
+ const i18n = setupI18n({ state });
99
+ setupFramepay({ state });
100
+
101
+ if (state.data.transaction && state.data.transaction?.type === 'setup') {
102
+ state.options.transactionType = 'setup';
211
103
  }
212
104
 
213
- const i18n = this.configs?.i18n || {};
214
- this.translate.init(this.options.locale, i18n);
215
-
216
- const {
217
- readyToPay,
218
- summary: summaryData,
219
- plans,
220
- products
221
- } = await FetchSummaryData.call(this, { riskMetadata });
222
-
223
- this.hasMounted = true;
224
-
225
- if (this.form) {
226
- this.formOptions = {
227
- summary: summaryData,
228
- mainStyle: this.mainStyle,
229
- readyToPay,
230
- plans,
231
- products,
232
- };
233
- this._mountMethodSelector(this.formOptions);
105
+ // Mount content
106
+ if (state.form) {
107
+ mountMethodSelector({ state });
234
108
  }
235
-
236
- if (this.summary) {
237
- this.summaryOptions = {
238
- summary: summaryData,
239
- readyToPay,
240
- plans,
241
- products,
242
- };
243
- this._mountSummary(this.summaryOptions);
109
+ if (state.summary) {
110
+ mountSummary({ state });
244
111
  }
245
- } catch(error) {
112
+ i18n({state});
113
+ state.hasMounted = true;
114
+ } catch (error) {
246
115
  throw error;
247
116
  }
248
117
 
249
- const i18n = this.configs?.i18n || {};
250
- this.translate.init(this.options.locale, i18n);
251
- this.translate.translateItems();
252
-
253
-
254
- if (this.options.features.autoConfirmation) {
255
- this.on('instrument-ready', (instrument) => {
256
- this.show('confirmation', {
257
- instrument,
258
- mainStyle: this.mainStyle
259
- });
118
+ if (state.options.features.autoConfirmation) {
119
+ on({
120
+ eventName: 'instrument-ready',
121
+ callback: (payload) => {
122
+ show({
123
+ componentName: 'confirmation',
124
+ payload,
125
+ state
126
+ });
127
+ }
260
128
  });
261
129
  }
262
130
 
263
- if (this.options.features.autoResult) {
264
- this.on('purchase-completed', (purchase) => {
265
- this.show('result', {
266
- purchase,
267
- mainStyle: this.mainStyle
131
+ if (state.options.features.autoResult) {
132
+ if (state.options.transactionType === 'setup') {
133
+ on({
134
+ eventName: 'setup-completed',
135
+ callback: (payload) => {
136
+ show({
137
+ componentName: 'result',
138
+ payload,
139
+ state
140
+ });
141
+ }
268
142
  });
269
- });
143
+ } else {
144
+ on({
145
+ eventName: 'purchase-completed',
146
+ callback: (payload) => {
147
+ show({
148
+ componentName: 'result',
149
+ payload,
150
+ state
151
+ });
152
+ }
153
+ });
154
+ }
270
155
  }
271
- };
156
+ }
@@ -1,5 +1,5 @@
1
- import { MockRebillyInstruments } from 'tests/mocks/rebilly-instruments-mock';
2
- import { createFramepayMock } from 'tests/mocks/framepay-mock';
1
+ import { RenderMockRebillyInstruments } from 'tests/mocks/rebilly-instruments-mock';
2
+ import { RebillyInstrumentsInstance } from '@rebilly/instruments';
3
3
  import { get, ok, post } from 'msw-when-then';
4
4
  import { when } from 'tests/msw/server';
5
5
  import { storefrontURL } from 'tests/mocks/storefront-api-mock';
@@ -8,171 +8,67 @@ import ProductModel from '@/storefront/models/product-model';
8
8
  import SummaryModel from '@/storefront/models/summary-model';
9
9
 
10
10
  describe('RebillyInstruments instance', () => {
11
- it('should throw error when there is no DOM element to mount the form', () => {
12
- document.body.innerHTML =
13
- `
14
- <div class="form-selector"></div>
15
- <div class="summary-selector"></div>
16
- `;
17
-
18
- const rebillyInstruments = MockRebillyInstruments();
19
- expect(async () => {
20
- await rebillyInstruments.mount({})
21
- }).rejects.toEqual(new Error('Could not find DOM element with CSS class or id ".rebilly-instruments" to mount form'));
22
- });
23
-
24
- it('should throw error when providing the wrong type for the form property', () => {
25
- document.body.innerHTML =
26
- `
27
- <div class="form-selector"></div>
28
- <div class="summary-selector"></div>
29
- `;
30
-
31
- const options = {
32
- form: []
33
- };
34
-
35
- const rebillyInstruments = MockRebillyInstruments();
36
- expect(async () => {
37
- await rebillyInstruments.mount(options)
38
- }).rejects.toEqual(new Error('Please provide a valid CSS class, id or DOM element for "form" property'));
39
- });
40
-
41
- it('should inject HTML to the merchant\'s website', async () => {
42
- const testPlan = new PlanModel({name: 'Test Plan', id: 'test-plan-id-1'});
43
- const testProduct = new ProductModel({description: 'My Awesome Product', id: 'test-product-1'});
44
- const testSummary = new SummaryModel(
45
- {
46
- currency: 'USD',
47
- lineItems: [
48
- {
49
- description: 'test-plan-id-1',
50
- planId: 'test-plan-id-1',
51
- productId: 'test-product-1',
52
- quantity: 1,
53
- }
54
- ]
55
- }
56
- );
11
+ it("should inject HTML to the merchant's website", async () => {
57
12
  const framePayScriptUrl = 'https://framepay.rebilly.com/rebilly.js';
58
13
  const framePayStyleUrl = 'https://dev.framepay.rebilly.com/rebilly.css';
59
14
 
60
- when(post(`${storefrontURL}/ready-to-pay`)).thenReturn((() => {
61
- return ok(
62
- [{
63
- method: 'payment-card',
64
- feature: {
65
- name: 'Google Pay',
66
- merchantName: 'google-pay-merchant-name',
67
- merchantOrigin: 'google-pay-merchant-origin'
68
- },
69
- brands: ['Visa', 'MasterCard', 'American Express', 'Discover'],
70
- filters: []
71
- }]
72
- );
73
- })());
74
-
75
- when(post(`${storefrontURL}/preview-purchase`)).thenReturn((() => {
76
- return ok(testSummary);
77
- })());
78
-
79
- when(get(`${storefrontURL}/plans`)).thenReturn((() => {
80
- return ok([testPlan]);
81
- })());
82
-
83
- when(get(`${storefrontURL}/products`)).thenReturn((() => {
84
- return ok([testProduct]);
85
- })());
86
-
87
- document.body.innerHTML =
88
- `
89
- <div class="form-selector"></div>
90
- <div class="summary-selector"></div>
91
- `;
92
-
93
15
  const options = {
94
16
  form: '.form-selector',
95
17
  summary: '.summary-selector',
96
- _dev: {
97
- framePayStyleLink: framePayStyleUrl
98
- },
99
- options: {
100
- locale: 'auto',
101
- intent: {
102
- items: [
103
- {
104
- planId: 'test-plan-id-1',
105
- quantity: 1
106
- }
107
- ]
18
+ locale: 'auto',
19
+ items: [
20
+ {
21
+ planId: 'test-plan-id-1',
22
+ quantity: 1
108
23
  }
109
- }
110
- };
111
-
112
- const framepayMock = createFramepayMock();
113
- global.Rebilly = framepayMock;
114
-
115
- const rebillyInstruments = MockRebillyInstruments({
24
+ ],
116
25
  theme: {
117
26
  color: {
118
27
  background: '#000'
119
28
  }
120
29
  },
121
30
  css: `
122
- .rebilly-instruments-summary-line-item-synopsis-title {
123
- color: rgb(0, 68, 212);
124
- }
125
- `
126
- });
127
- await rebillyInstruments.mount(options);
31
+ .rebilly-instruments-summary-line-item-synopsis-title {
32
+ color: rgb(0, 68, 212);
33
+ }
34
+ `,
35
+ _dev: {
36
+ framePayStyleLink: framePayStyleUrl
37
+ },
38
+ };
39
+
40
+ await RenderMockRebillyInstruments(options);
128
41
 
129
42
  // Mounts form and summary
130
43
  const summarySelector = document.querySelector('.summary-selector');
131
- expect(summarySelector.innerHTML).toMatch(testPlan.name);
44
+ expect(summarySelector.innerHTML).toMatch('My Product');
132
45
 
133
46
  // Theme config overrides initial styles
134
- const SUMMARY_CONTAINER = summarySelector.querySelector('.rebilly-instruments-content');
135
- expect(getComputedStyle(SUMMARY_CONTAINER).background).toEqual('rgb(0, 0, 0)');
47
+ const SUMMARY_CONTAINER = summarySelector.querySelector(
48
+ '.rebilly-instruments-content'
49
+ );
50
+ expect(getComputedStyle(SUMMARY_CONTAINER).background).toEqual(
51
+ 'rgb(0, 0, 0)'
52
+ );
136
53
 
137
54
  // CSS config property overrides initial styles
138
- const LINE_ITEM_TITLE = document.querySelector('.rebilly-instruments-summary-line-item-synopsis-title');
55
+ const LINE_ITEM_TITLE = document.querySelector(
56
+ '.rebilly-instruments-summary-line-item-synopsis-title'
57
+ );
139
58
  expect(getComputedStyle(LINE_ITEM_TITLE).color).toEqual('rgb(0, 68, 212)');
140
59
 
141
60
  // Mounts default FramePay script
142
61
  const SCRIPTS = [...document.querySelectorAll('head script')];
143
- const FRAMEPAY_SCRIPT = SCRIPTS.find(script => script.src === framePayScriptUrl);
62
+ const FRAMEPAY_SCRIPT = SCRIPTS.find(
63
+ (script) => script.src === framePayScriptUrl
64
+ );
144
65
  expect(FRAMEPAY_SCRIPT.src).toEqual(framePayScriptUrl);
145
66
 
146
67
  // Mounts _dev FramePay style
147
68
  const STYLE_LINKS = [...document.querySelectorAll('head link')];
148
- const FRAMEPAY_STYLE = STYLE_LINKS.find(script => script.href === framePayStyleUrl);
69
+ const FRAMEPAY_STYLE = STYLE_LINKS.find(
70
+ (script) => script.href === framePayStyleUrl
71
+ );
149
72
  expect(FRAMEPAY_STYLE.href).toEqual(framePayStyleUrl);
150
-
151
- // objectContaining allows matching a subset (this could be done with sinon + mocha too)
152
- expect(framepayMock.initialize).toHaveBeenCalledWith(expect.objectContaining({
153
- digitalWallet: {
154
- googlePayDisplayOptions: {
155
- buttonColor: 'black',
156
- buttonHeight: '44px',
157
- buttonType: 'short',
158
- },
159
- merchantConfig: {
160
- merchantName: 'google-pay-merchant-name',
161
- merchantOrigin: 'google-pay-merchant-origin',
162
- },
163
- transactionData: {
164
- amount: 0,
165
- countryCode: 'US',
166
- currency: 'USD',
167
- label: 'test-website-id',
168
- },
169
- },
170
- organizationId: 'test-organization-id',
171
- publishableKey: 'test-api-key',
172
- }));
173
-
174
- //Simulate that framepay is ready
175
- framepayMock.simulateEvent('ready');
176
73
  });
177
74
  });
178
-
@@ -0,0 +1,26 @@
1
+ import { processPropertyAsDOMElement } from '../../utils';
2
+
3
+ export default ({element = '', options = {}}) => {
4
+ if (element !== 'form' && element !== 'summary') {
5
+ throw new Error('element must be "form" or "summary"');
6
+ }
7
+
8
+ const getProp = () => {
9
+ if (options[element]) {
10
+ return options[element]
11
+ }
12
+ switch(element) {
13
+ case 'summary':
14
+ return '.rebilly-instruments-summary'
15
+ case 'form':
16
+ default:
17
+ return '.rebilly-instruments'
18
+ }
19
+ }
20
+
21
+ return processPropertyAsDOMElement({
22
+ prop: getProp(),
23
+ propName: element,
24
+ isRequired: ['form'].includes(element)
25
+ });
26
+ }