@rebilly/instruments 1.0.1-beta → 1.0.2-beta.10

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 (277) hide show
  1. package/.babelrc +23 -5
  2. package/.eslintrc.js +27 -0
  3. package/.prettierrc.js +11 -0
  4. package/CONTRIBUTING.md +4 -0
  5. package/README.md +361 -2
  6. package/dist/events/base-event.js +51 -37
  7. package/dist/events/events.spec.js +18 -0
  8. package/dist/events/index.js +11 -8
  9. package/dist/functions/destroy.js +27 -5
  10. package/dist/functions/destroy.spec.js +69 -0
  11. package/dist/functions/initialize.js +60 -41
  12. package/dist/functions/initialize.spec.js +13 -13
  13. package/dist/functions/mount/fetch-summary-data.js +46 -0
  14. package/dist/functions/mount/fetch-summary-data.spec.js +44 -0
  15. package/dist/functions/mount/index.js +346 -0
  16. package/dist/functions/mount/mount.spec.js +135 -0
  17. package/dist/functions/on.js +26 -18
  18. package/dist/functions/on.spec.js +45 -63
  19. package/dist/functions/purchase.js +41 -154
  20. package/dist/functions/purchase.spec.js +60 -76
  21. package/dist/functions/show.js +39 -43
  22. package/dist/functions/show.spec.js +57 -0
  23. package/dist/functions/update.js +60 -5
  24. package/dist/functions/update.spec.js +100 -0
  25. package/dist/i18n/en.json +19 -0
  26. package/dist/i18n/es.json +19 -0
  27. package/dist/i18n/i18n.spec.js +6 -23
  28. package/dist/i18n/index.js +44 -67
  29. package/dist/index.js +70 -71
  30. package/dist/index.spec.js +24 -44
  31. package/dist/loader/index.js +63 -62
  32. package/dist/loader/loader.spec.js +14 -11
  33. package/dist/storefront/index.js +28 -39
  34. package/dist/storefront/models/plan-model.js +37 -54
  35. package/dist/storefront/models/product-model.js +25 -36
  36. package/dist/storefront/models/ready-to-pay-model.js +38 -42
  37. package/dist/storefront/models/summary-model.js +72 -99
  38. package/dist/storefront/plans.js +37 -84
  39. package/dist/storefront/plans.spec.js +61 -151
  40. package/dist/storefront/products.js +35 -83
  41. package/dist/storefront/products.spec.js +60 -150
  42. package/dist/storefront/purchase.js +27 -64
  43. package/dist/storefront/purchase.spec.js +51 -87
  44. package/dist/storefront/ready-to-pay.js +45 -107
  45. package/dist/storefront/ready-to-pay.spec.js +72 -147
  46. package/dist/storefront/storefront.spec.js +6 -6
  47. package/dist/storefront/summary.js +37 -84
  48. package/dist/storefront/summary.spec.js +135 -240
  49. package/dist/style/base/__snapshots__/theme.spec.js.snap +52 -0
  50. package/dist/style/base/index.js +72 -0
  51. package/dist/style/base/theme.js +73 -0
  52. package/dist/style/base/theme.spec.js +30 -0
  53. package/dist/style/browserslist.js +8 -0
  54. package/dist/style/components/address.js +64 -0
  55. package/dist/style/components/button.js +61 -0
  56. package/dist/style/components/divider.js +48 -0
  57. package/dist/style/components/forms/checkbox.js +83 -0
  58. package/dist/style/components/forms/field.js +53 -0
  59. package/dist/style/components/forms/form.js +28 -0
  60. package/dist/style/components/forms/input.js +45 -0
  61. package/dist/style/components/forms/label.js +43 -0
  62. package/dist/style/components/forms/select.js +63 -0
  63. package/dist/style/components/forms/validation.js +34 -0
  64. package/dist/style/components/icons.js +22 -0
  65. package/dist/style/components/index.js +57 -0
  66. package/dist/style/components/loader.js +48 -0
  67. package/dist/style/components/methods.js +104 -0
  68. package/dist/style/components/overlay.js +33 -0
  69. package/dist/style/helpers/index.js +59 -0
  70. package/dist/style/index.js +48 -0
  71. package/dist/style/payment-instruments/content.js +17 -0
  72. package/dist/style/payment-instruments/index.js +20 -0
  73. package/dist/style/payment-instruments/payment-card.js +35 -0
  74. package/dist/style/utils/color-values.js +22 -0
  75. package/dist/style/vendor/framepay.js +34 -0
  76. package/dist/style/vendor/postmate.js +17 -0
  77. package/dist/style/views/confirmation.js +85 -0
  78. package/dist/style/views/index.js +29 -0
  79. package/dist/style/views/method-selector.js +20 -0
  80. package/dist/style/views/modal.js +93 -0
  81. package/dist/style/views/result.js +61 -0
  82. package/dist/style/views/summary.js +123 -0
  83. package/dist/utils/add-dom-element.js +12 -34
  84. package/dist/utils/format-currency.js +4 -4
  85. package/dist/utils/has-valid-css-selector.js +2 -2
  86. package/dist/utils/index.js +15 -31
  87. package/dist/utils/is-dom-element.js +1 -1
  88. package/dist/utils/process-property-as-dom-element.js +12 -17
  89. package/dist/utils/sleep.js +10 -0
  90. package/{src/components → dist/views}/__snapshots__/summary.spec.js.snap +7 -3
  91. package/dist/views/common/iframe/base-iframe.js +57 -0
  92. package/dist/views/common/iframe/event-listeners.js +50 -0
  93. package/dist/views/common/iframe/index.js +19 -0
  94. package/dist/views/common/iframe/method-iframe.js +33 -0
  95. package/dist/views/common/iframe/modal-iframe.js +38 -0
  96. package/dist/views/common/iframe/view-iframe.js +31 -0
  97. package/dist/views/common/render-utilities.js +11 -0
  98. package/dist/views/confirmation.js +82 -0
  99. package/dist/views/method-selector/__snapshots__/method-selector.spec.js.snap +3 -0
  100. package/dist/views/method-selector/express-methods/apple-pay.js +92 -0
  101. package/dist/views/method-selector/express-methods/google-pay.js +32 -0
  102. package/dist/views/method-selector/express-methods/paypal.js +19 -0
  103. package/dist/views/method-selector/generate-digital-wallet.js +59 -0
  104. package/dist/views/method-selector/generate-digital-wallet.spec.js +132 -0
  105. package/dist/views/method-selector/get-method-data.js +25 -0
  106. package/dist/views/method-selector/get-payment-methods.js +55 -0
  107. package/dist/views/method-selector/get-payment-methods.spec.js +44 -0
  108. package/dist/views/method-selector/index.js +133 -0
  109. package/dist/views/method-selector/method-selector.spec.js +139 -0
  110. package/dist/views/method-selector/mount-express-methods.js +69 -0
  111. package/dist/views/method-selector/mount-methods.js +78 -0
  112. package/dist/views/modal.js +83 -0
  113. package/dist/views/result.js +42 -0
  114. package/dist/views/summary.js +162 -0
  115. package/dist/views/summary.spec.js +148 -0
  116. package/package.json +12 -6
  117. package/src/events/base-event.js +35 -12
  118. package/src/events/events.spec.js +11 -0
  119. package/src/events/index.js +12 -6
  120. package/src/functions/destroy.js +22 -3
  121. package/src/functions/destroy.spec.js +63 -0
  122. package/src/functions/initialize.js +43 -20
  123. package/src/functions/initialize.spec.js +9 -7
  124. package/src/functions/mount/fetch-summary-data.js +29 -0
  125. package/src/functions/mount/fetch-summary-data.spec.js +41 -0
  126. package/src/functions/mount/index.js +312 -0
  127. package/src/functions/mount/mount.spec.js +171 -0
  128. package/src/functions/on.js +17 -14
  129. package/src/functions/on.spec.js +39 -29
  130. package/src/functions/purchase.js +24 -64
  131. package/src/functions/purchase.spec.js +19 -17
  132. package/src/functions/show.js +27 -7
  133. package/src/functions/show.spec.js +61 -0
  134. package/src/functions/update.js +50 -3
  135. package/src/functions/update.spec.js +107 -0
  136. package/src/i18n/i18n.spec.js +6 -4
  137. package/src/i18n/index.js +20 -12
  138. package/src/index.js +43 -49
  139. package/src/index.spec.js +11 -42
  140. package/src/loader/index.js +55 -39
  141. package/src/loader/loader.spec.js +30 -23
  142. package/src/storefront/index.js +9 -7
  143. package/src/storefront/models/plan-model.js +1 -1
  144. package/src/storefront/models/product-model.js +1 -1
  145. package/src/storefront/models/ready-to-pay-model.js +10 -4
  146. package/src/storefront/models/summary-model.js +8 -15
  147. package/src/storefront/plans.js +16 -12
  148. package/src/storefront/plans.spec.js +29 -37
  149. package/src/storefront/products.js +16 -12
  150. package/src/storefront/products.spec.js +28 -39
  151. package/src/storefront/purchase.js +8 -6
  152. package/src/storefront/purchase.spec.js +18 -17
  153. package/src/storefront/ready-to-pay.js +19 -13
  154. package/src/storefront/ready-to-pay.spec.js +41 -41
  155. package/src/storefront/storefront.spec.js +1 -1
  156. package/src/storefront/summary.js +14 -12
  157. package/src/storefront/summary.spec.js +37 -50
  158. package/src/style/base/__snapshots__/theme.spec.js.snap +52 -0
  159. package/src/style/base/index.js +63 -0
  160. package/src/style/base/theme.js +61 -0
  161. package/src/style/base/theme.spec.js +32 -0
  162. package/src/style/browserslist.js +1 -0
  163. package/src/style/components/address.js +55 -0
  164. package/src/style/components/button.js +54 -0
  165. package/src/style/components/divider.js +39 -0
  166. package/src/style/components/forms/checkbox.js +76 -0
  167. package/src/style/components/forms/field.js +44 -0
  168. package/src/style/components/forms/form.js +19 -0
  169. package/src/style/components/forms/input.js +36 -0
  170. package/src/style/components/forms/label.js +34 -0
  171. package/src/style/components/forms/select.js +54 -0
  172. package/src/style/components/forms/validation.js +25 -0
  173. package/src/style/components/icons.js +13 -0
  174. package/src/style/components/index.js +35 -0
  175. package/src/style/components/loader.js +41 -0
  176. package/src/style/components/methods.js +93 -0
  177. package/src/style/components/overlay.js +24 -0
  178. package/src/style/helpers/index.js +51 -0
  179. package/src/style/index.js +30 -0
  180. package/src/style/payment-instruments/content.js +8 -0
  181. package/src/style/payment-instruments/index.js +10 -0
  182. package/src/style/payment-instruments/payment-card.js +26 -0
  183. package/src/style/utils/color-values.js +9 -0
  184. package/src/style/vendor/framepay.js +25 -0
  185. package/src/style/vendor/postmate.js +8 -0
  186. package/src/style/views/confirmation.js +76 -0
  187. package/src/style/views/index.js +16 -0
  188. package/src/style/views/method-selector.js +11 -0
  189. package/src/style/views/modal.js +84 -0
  190. package/src/style/views/result.js +52 -0
  191. package/src/style/views/summary.js +114 -0
  192. package/src/utils/add-dom-element.js +12 -13
  193. package/src/utils/format-currency.js +4 -1
  194. package/src/utils/has-valid-css-selector.js +2 -2
  195. package/src/utils/index.js +2 -6
  196. package/src/utils/is-dom-element.js +1 -1
  197. package/src/utils/process-property-as-dom-element.js +27 -24
  198. package/src/utils/sleep.js +3 -0
  199. package/src/views/__snapshots__/summary.spec.js.snap +292 -0
  200. package/src/views/common/iframe/base-iframe.js +46 -0
  201. package/src/views/common/iframe/event-listeners.js +27 -0
  202. package/src/views/common/iframe/index.js +7 -0
  203. package/src/views/common/iframe/method-iframe.js +21 -0
  204. package/src/views/common/iframe/modal-iframe.js +27 -0
  205. package/src/views/common/iframe/view-iframe.js +18 -0
  206. package/src/views/common/render-utilities.js +4 -0
  207. package/src/views/confirmation.js +57 -0
  208. package/src/views/method-selector/__snapshots__/method-selector.spec.js.snap +3 -0
  209. package/src/views/method-selector/express-methods/apple-pay.js +78 -0
  210. package/src/views/method-selector/express-methods/google-pay.js +25 -0
  211. package/src/views/method-selector/express-methods/paypal.js +7 -0
  212. package/src/views/method-selector/generate-digital-wallet.js +44 -0
  213. package/src/views/method-selector/generate-digital-wallet.spec.js +131 -0
  214. package/src/{components/form → views/method-selector}/get-method-data.js +9 -5
  215. package/src/views/method-selector/get-payment-methods.js +40 -0
  216. package/src/views/method-selector/get-payment-methods.spec.js +40 -0
  217. package/src/views/method-selector/index.js +110 -0
  218. package/src/views/method-selector/method-selector.spec.js +146 -0
  219. package/src/views/method-selector/mount-express-methods.js +53 -0
  220. package/src/views/method-selector/mount-methods.js +71 -0
  221. package/src/views/modal.js +84 -0
  222. package/src/views/result.js +30 -0
  223. package/src/{components → views}/summary.js +90 -21
  224. package/src/views/summary.spec.js +170 -0
  225. package/tests/async-utilities.js +22 -0
  226. package/tests/mocks/rebilly-instruments-mock.js +105 -7
  227. package/dist/components/confirmation.js +0 -103
  228. package/dist/components/form/form.js +0 -110
  229. package/dist/components/form/form.spec.js +0 -135
  230. package/dist/components/form/get-method-data.js +0 -21
  231. package/dist/components/form/get-payment-methods.js +0 -42
  232. package/dist/components/form/method-selector.js +0 -61
  233. package/dist/components/form/mount-express-payment-methods.js +0 -102
  234. package/dist/components/form/process-digital-wallet-options.js +0 -20
  235. package/dist/components/form/zoid-helpers.js +0 -130
  236. package/dist/components/result.js +0 -66
  237. package/dist/components/summary.js +0 -60
  238. package/dist/components/summary.spec.js +0 -144
  239. package/dist/events/instrument-ready.js +0 -51
  240. package/dist/events/purchase-complete.js +0 -51
  241. package/dist/functions/mount.js +0 -311
  242. package/dist/functions/mount.spec.js +0 -203
  243. package/dist/styles/base-styles.js +0 -12
  244. package/dist/styles/flat-theme-object.js +0 -42
  245. package/dist/styles/framepay.js +0 -15
  246. package/dist/styles/main.js +0 -25
  247. package/dist/styles/payment-card.js +0 -12
  248. package/dist/styles/shade-tint-values-helper.js +0 -28
  249. package/dist/styles/style-variables.js +0 -43
  250. package/dist/utils/camel-case.js +0 -12
  251. package/dist/utils/kebab-case.js +0 -10
  252. package/dist/utils/un-kebab-case.js +0 -10
  253. package/src/components/confirmation.js +0 -77
  254. package/src/components/form/__snapshots__/form.spec.js.snap +0 -43
  255. package/src/components/form/form.js +0 -88
  256. package/src/components/form/form.spec.js +0 -109
  257. package/src/components/form/get-payment-methods.js +0 -32
  258. package/src/components/form/method-selector.js +0 -47
  259. package/src/components/form/mount-express-payment-methods.js +0 -84
  260. package/src/components/form/process-digital-wallet-options.js +0 -11
  261. package/src/components/form/zoid-helpers.js +0 -114
  262. package/src/components/result.js +0 -50
  263. package/src/components/summary.spec.js +0 -106
  264. package/src/events/instrument-ready.js +0 -11
  265. package/src/events/purchase-complete.js +0 -11
  266. package/src/functions/mount.js +0 -204
  267. package/src/functions/mount.spec.js +0 -172
  268. package/src/styles/base-styles.js +0 -741
  269. package/src/styles/flat-theme-object.js +0 -12
  270. package/src/styles/framepay.js +0 -30
  271. package/src/styles/main.js +0 -17
  272. package/src/styles/payment-card.js +0 -18
  273. package/src/styles/shade-tint-values-helper.js +0 -13
  274. package/src/styles/style-variables.js +0 -34
  275. package/src/utils/camel-case.js +0 -3
  276. package/src/utils/kebab-case.js +0 -3
  277. package/src/utils/un-kebab-case.js +0 -3
@@ -0,0 +1,29 @@
1
+ import { fetchPlans } from '../../storefront/plans';
2
+ import { fetchProducts } from '../../storefront/products';
3
+ import { fetchReadyToPay } from '../../storefront/ready-to-pay';
4
+ import { fetchSummary } from '../../storefront/summary';
5
+
6
+ export async function fetchSummaryData({
7
+ state = null,
8
+ riskMetadata = null,
9
+ summaryPayload = null
10
+ }) {
11
+ if (!riskMetadata) {
12
+ throw new Error('riskMetadata is required for FetchSummaryData');
13
+ }
14
+
15
+ const [readyToPay, summary] = await Promise.all([
16
+ fetchReadyToPay({ riskMetadata, state }),
17
+ fetchSummary({ data: summaryPayload, state })
18
+ ]);
19
+
20
+ const plans = await fetchPlans({ data: summary, state });
21
+ const products = await fetchProducts({ data: plans, state });
22
+
23
+ return {
24
+ readyToPay,
25
+ summary,
26
+ plans,
27
+ products
28
+ };
29
+ }
@@ -0,0 +1,41 @@
1
+ import { fetchSummaryData } from './fetch-summary-data';
2
+
3
+ describe.skip('Fetch Summary Data function helper', () => {
4
+ class TestInstance {
5
+ constructor() {
6
+ this.storefront = jest.fn();
7
+ this.configs = jest.fn();
8
+ this.options = jest.fn();
9
+
10
+ this._fetchSummary = jest.fn(() => {
11
+ return new Promise((resolve) => resolve({}));
12
+ });
13
+
14
+ this._fetchProducts = jest.fn();
15
+ }
16
+ }
17
+
18
+ it.skip('should fetch all the data', async () => {
19
+ const instance = new TestInstance();
20
+ await fetchSummaryData({ riskMetadata: {}, state: instance });
21
+ expect(fetchReadyToPay).toBeCalledTimes(1);
22
+ expect(instance._fetchSummary).toBeCalledTimes(1);
23
+ expect(instance._fetchPlans).toBeCalledTimes(1);
24
+ expect(instance._fetchProducts).toBeCalledTimes(1);
25
+ });
26
+
27
+ it('should pass riskMetadata to ready to pay', async () => {
28
+ const testRiskMetadata = {};
29
+ const instance = new TestInstance();
30
+
31
+ await fetchSummaryData({ riskMetadata: testRiskMetadata, state: instance });
32
+
33
+ expect(instance._fetchReadyToPay).toBeCalledWith(testRiskMetadata);
34
+
35
+ expect(async () => {
36
+ await fetchSummaryData({ state: instance });
37
+ }).rejects.toEqual(
38
+ new Error('riskMetadata is required for FetchSummaryData')
39
+ );
40
+ });
41
+ });
@@ -0,0 +1,312 @@
1
+ /* eslint-disable max-len */
2
+ import { collectData } from '@rebilly/risk-data-collector';
3
+ import merge from 'lodash.merge';
4
+ import { mountSummary } from '../../views/summary';
5
+ import { mountMethodSelector } from '../../views/method-selector';
6
+ import { mainStyle } from '../../style';
7
+ import { addDOMElement, processPropertyAsDOMElement } from '../../utils';
8
+ import { fetchSummaryData } from './fetch-summary-data';
9
+ import { show } from '../show';
10
+ import { on } from '../on';
11
+
12
+ /**
13
+ * @typedef {object} Item
14
+ * @property {string} planId - The Rebilly id of the plan.
15
+ * @property {number} quantity - The number of the plans to be purchased.
16
+ * @property {string} thumbnail - The source img for the product. Recommend 100px by 100px.
17
+ */
18
+
19
+ /**
20
+ * @typedef {object} Intent
21
+ * @property {"purchase" | "vault"} mode - Which mode the mount is usings. Default "purchase".
22
+ * @property {Array.<Item>} items - Which plans the customer is purchasing.
23
+ * @property {string} customerId - Which customer is associated with the instrument.
24
+ * @property {string} countryCode - The country code for the transaction
25
+ */
26
+
27
+ /**
28
+ * @typedef {GooglePayDisplayOptions} GooglePay
29
+ * @param {"back" | "white"} buttonColor - default "black". Color of google pay button
30
+ * @param {"short" | "long"} buttonType - default "short". The length of the button
31
+ * @param {string} buttonHeight - The value and units of the button to match other payment buttons
32
+ * <br>example: "44px", "1rem" etc.
33
+ */
34
+
35
+ /**
36
+ * @typedef {object} GooglePay
37
+ * @param {GooglePayDisplayOptions} displayOptions - display options for google pay instrument
38
+ */
39
+
40
+ /**
41
+ * @typedef {object} PaymentCard
42
+ * @param {boolean} popup - default: false. Show method as a button with a form popup
43
+ * <br>Otherwise the form will be mounted inline.
44
+ */
45
+
46
+ /**
47
+ * @typedef {object} Address
48
+ * @param {string} name - default: default. One of default, combined, or stacked.
49
+ * @param {string} region - default: default. One of default, split, or stacked.
50
+ * @param {Array.<"organization" | "phoneNumber">} show. Show extra fields listed.
51
+ * @param {Array.<"address" | "address2" | "email | "country" | "region" | "postalCode"">} hide. Hide the listed fields.
52
+ * @param {Array.<"organization" | "address" | "address2" | "email" | "phoneNumber" | "country" | "region" | "postalCode">} require.
53
+ * <br>If the field name is included, enforce the data for those inputs to be included.
54
+ * <br>First name and last name are always required. Country is always required if the products require shipping.
55
+ */
56
+
57
+ /**
58
+ * @typedef {object} PaymentInstruments
59
+ * @param {boolean} compactExpressInstruments - default: true. Show express methods as
60
+ * <br>inline pill buttons, or list of full width button.
61
+ * @param {PaymentCard} paymentCard - settings for payment card instruments
62
+ * @param {GooglePay} googlePay - settings for google pay instruments
63
+ * @param {Address} address - customization for address components for all payment instruments.
64
+ */
65
+
66
+ /**
67
+ * @typedef {object} Features
68
+ * @param {boolean} autoConfirmation - default: true. Will mount the confirmation screen after `instrument-ready`
69
+ * <br>event is triggered. Will need to trigger purchase manually if set to false.
70
+ * <br>Can use RebillyInstruments.show('confirmation', options) to mount defautl component
71
+ * @param {boolean} autoResult - default: true. Show results of transaction after `purchase-completed` event is triggered
72
+ * <br>Will need to handle purchase result manually if set to false.
73
+ * <br>Can use RebillyInstruments.show('result', options) to display default component
74
+ */
75
+
76
+ /**
77
+ * @typedef {object} Options
78
+ * @property {Intent} intent - The information required for purchaseing or vaulting an instrument
79
+ * @property {PaymentInstruments} paymentInstruments - settings for various payment instruments
80
+ * @property {Features} features - flags to enable and disable different features
81
+ * @property {string} locale - default: auto. Language to render component text
82
+ */
83
+
84
+ /**
85
+ * Mount library with configurations.
86
+ * @typedef {object} MountParams
87
+ * @property {Object} state - Global state
88
+ * @property {string | HTMLElement} form - The CSS class or HTML element were the form will be mounted.
89
+ * @property {string | HTMLElement} form - The CSS class or HTML element were the form will be mounted.
90
+ * @property {string | HTMLElement} summary - The CSS class or HTML element were the summary will be mounted.
91
+ * @property {Options} options - The configurations that are to be passed to the library for use.
92
+ */
93
+
94
+ /**
95
+ * Mount library with configurations.
96
+ * @param {MountParams} params
97
+ */
98
+ export async function mount({
99
+ state,
100
+ form = '.rebilly-instruments',
101
+ summary = '.rebilly-instruments-summary',
102
+ options = {},
103
+ _dev = null
104
+ }) {
105
+ state.form = form;
106
+ state.summary = summary;
107
+ state.mainStyle = null;
108
+ state._dev = _dev;
109
+
110
+ const framePayUrls = {
111
+ script: _dev
112
+ ? _dev.framePayScriptLink || 'https://framepay.rebilly.com/rebilly.js'
113
+ : 'https://framepay.rebilly.com/rebilly.js',
114
+ style: _dev
115
+ ? _dev.framePayStyleLink || 'https://framepay.rebilly.com/rebilly.css'
116
+ : 'https://framepay.rebilly.com/rebilly.css'
117
+ };
118
+ const _computed = {
119
+ paymentMethodsUrl: _dev
120
+ ? _dev.paymentMethodsUrl || 'https://forms.local.rebilly.dev:3000'
121
+ : 'https://forms.secure-payments.app'
122
+ };
123
+
124
+ const OPTIONS_DEFAULTS = {
125
+ intent: {
126
+ countryCode: 'US'
127
+ },
128
+ locale: 'auto',
129
+ paymentInstruments: {
130
+ address: {
131
+ name: 'default',
132
+ region: 'default',
133
+ hide: [],
134
+ show: [],
135
+ require: []
136
+ },
137
+ compactExpressInstruments: true,
138
+ googlePay: {
139
+ displayOptions: {
140
+ buttonColor: 'black',
141
+ buttonType: 'short',
142
+ buttonHeight: '44px'
143
+ }
144
+ },
145
+ applePay: {
146
+ displayOptions: {
147
+ buttonColor: 'black',
148
+ buttonType: 'plain',
149
+ buttonHeight: '44px'
150
+ }
151
+ },
152
+ paymentCard: {
153
+ popup: false
154
+ }
155
+ },
156
+ features: {
157
+ autoConfirmation: true,
158
+ autoResult: true
159
+ }
160
+ };
161
+
162
+ const combinedOptions = { ...options, _computed };
163
+ if (_dev) {
164
+ combinedOptions._dev = _dev;
165
+ }
166
+
167
+ state.options = merge(OPTIONS_DEFAULTS, combinedOptions);
168
+ state.form = processPropertyAsDOMElement({
169
+ prop: state.form,
170
+ propName: 'form'
171
+ });
172
+ state.summary = processPropertyAsDOMElement({
173
+ prop: state.summary,
174
+ propName: 'summary',
175
+ isRequired: false
176
+ });
177
+
178
+ // Setup loader
179
+ state.loader.addDOMElement({ el: state.form });
180
+ state.loader.addDOMElement({ section: 'summary', el: state.summary });
181
+
182
+ // Adds base stylesheet
183
+ state.mainStyle = await mainStyle(state.configs?.theme || {});
184
+ addDOMElement({
185
+ element: 'style',
186
+ attributes: { type: 'text/css' },
187
+ content: state.mainStyle,
188
+ target: 'head'
189
+ });
190
+
191
+ // Adds configs CSS to override any styles
192
+ if (state.configs.css) {
193
+ addDOMElement({
194
+ element: 'style',
195
+ attributes: { type: 'text/css' },
196
+ content: state.configs.css,
197
+ target: 'head'
198
+ });
199
+ }
200
+
201
+ // Adds FramePay
202
+ if (!document.querySelectorAll('[framepay*="script"]').length) {
203
+ addDOMElement({
204
+ element: 'script',
205
+ attributes: {
206
+ framepay: 'script',
207
+ src: framePayUrls.script
208
+ },
209
+ target: 'head'
210
+ });
211
+ }
212
+
213
+ if (!document.querySelectorAll('[framepay*="stylesheet"]').length) {
214
+ addDOMElement({
215
+ element: 'link',
216
+ attributes: {
217
+ framepay: 'stylesheet',
218
+ href: framePayUrls.style,
219
+ rel: 'stylesheet'
220
+ },
221
+ target: 'head'
222
+ });
223
+ }
224
+
225
+ try {
226
+ state.loader.startLoading({ section: 'summary', id: 'initSummary' });
227
+ state.loader.startLoading({ id: 'initForm' });
228
+
229
+ const { riskMetadata } = await collectData();
230
+
231
+ if (
232
+ state.options.locale === 'auto' &&
233
+ riskMetadata?.browserData?.language
234
+ ) {
235
+ const {
236
+ browserData: { language }
237
+ } = riskMetadata;
238
+ state.options.locale = language;
239
+ }
240
+
241
+ const i18n = state.configs?.i18n || {};
242
+ state.translate.init(state.options.locale, i18n);
243
+
244
+ const {
245
+ readyToPay,
246
+ summary: summaryData,
247
+ plans,
248
+ products
249
+ } = await fetchSummaryData({ riskMetadata, state });
250
+
251
+ state.hasMounted = true;
252
+
253
+ if (state.form) {
254
+ const formOptions = {
255
+ summary: summaryData,
256
+ mainStyle: state.mainStyle,
257
+ readyToPay,
258
+ plans,
259
+ products
260
+ };
261
+ mountMethodSelector({ state, formOptions });
262
+ }
263
+
264
+ if (state.summary) {
265
+ const summaryOptions = {
266
+ summary: summaryData,
267
+ readyToPay,
268
+ plans,
269
+ products
270
+ };
271
+ mountSummary({ state, summaryOptions });
272
+ }
273
+ } catch (error) {
274
+ throw error;
275
+ }
276
+
277
+ const i18n = state.configs?.i18n || {};
278
+ state.translate.init(state.options.locale, i18n);
279
+ state.translate.translateItems();
280
+
281
+ if (state.options.features.autoConfirmation) {
282
+ on({
283
+ eventName: 'instrument-ready',
284
+ callback: (instrument) => {
285
+ show({
286
+ componentName: 'confirmation',
287
+ payload: {
288
+ instrument,
289
+ mainStyle: state.mainStyle
290
+ },
291
+ state
292
+ });
293
+ }
294
+ });
295
+ }
296
+
297
+ if (state.options.features.autoResult) {
298
+ on({
299
+ eventName: 'purchase-completed',
300
+ callback: (purchase) => {
301
+ show({
302
+ componentName: 'result',
303
+ payload: {
304
+ purchase,
305
+ mainStyle: state.mainStyle
306
+ },
307
+ state
308
+ });
309
+ }
310
+ });
311
+ }
312
+ }
@@ -0,0 +1,171 @@
1
+ import { MockRebillyInstruments } from 'tests/mocks/rebilly-instruments-mock';
2
+ import { get, ok, post } from 'msw-when-then';
3
+ import { when } from 'tests/msw/server';
4
+ import { storefrontURL } from 'tests/mocks/storefront-api-mock';
5
+ import PlanModel from '@/storefront/models/plan-model';
6
+ import ProductModel from '@/storefront/models/product-model';
7
+ import SummaryModel from '@/storefront/models/summary-model';
8
+
9
+ describe('RebillyInstruments instance', () => {
10
+ it('should throw error when there is no DOM element to mount the form', () => {
11
+ document.body.innerHTML = `
12
+ <div class="form-selector"></div>
13
+ <div class="summary-selector"></div>
14
+ `;
15
+
16
+ const rebillyInstruments = MockRebillyInstruments();
17
+ expect(async () => {
18
+ await rebillyInstruments.mount({});
19
+ }).rejects.toEqual(
20
+ new Error(
21
+ 'Could not find DOM element with CSS class or id ".rebilly-instruments" to mount form'
22
+ )
23
+ );
24
+ });
25
+
26
+ it('should throw error when providing the wrong type for the form property', () => {
27
+ document.body.innerHTML = `
28
+ <div class="form-selector"></div>
29
+ <div class="summary-selector"></div>
30
+ `;
31
+
32
+ const options = {
33
+ form: []
34
+ };
35
+
36
+ const rebillyInstruments = MockRebillyInstruments();
37
+ expect(async () => {
38
+ await rebillyInstruments.mount(options);
39
+ }).rejects.toEqual(
40
+ new Error(
41
+ 'Please provide a valid CSS class, id or DOM element for "form" property'
42
+ )
43
+ );
44
+ });
45
+
46
+ it.only("should inject HTML to the merchant's website", async () => {
47
+ const testPlan = new PlanModel({ name: 'Test Plan', id: 'test-plan-id-1' });
48
+ const testProduct = new ProductModel({
49
+ description: 'My Awesome Product',
50
+ id: 'test-product-1'
51
+ });
52
+ const testSummary = new SummaryModel({
53
+ currency: 'USD',
54
+ lineItems: [
55
+ {
56
+ description: 'test-plan-id-1',
57
+ planId: 'test-plan-id-1',
58
+ productId: 'test-product-1',
59
+ quantity: 1
60
+ }
61
+ ]
62
+ });
63
+ const framePayScriptUrl = 'https://framepay.rebilly.com/rebilly.js';
64
+ const framePayStyleUrl = 'https://dev.framepay.rebilly.com/rebilly.css';
65
+
66
+ when(post(`${storefrontURL}/ready-to-pay`)).thenReturn(
67
+ (() => {
68
+ return ok([
69
+ {
70
+ method: 'payment-card',
71
+ feature: {
72
+ name: 'Google Pay',
73
+ merchantName: 'google-pay-merchant-name',
74
+ merchantOrigin: 'google-pay-merchant-origin'
75
+ },
76
+ brands: ['Visa', 'MasterCard', 'American Express', 'Discover'],
77
+ filters: []
78
+ }
79
+ ]);
80
+ })()
81
+ );
82
+
83
+ when(post(`${storefrontURL}/preview-purchase`)).thenReturn(
84
+ (() => {
85
+ return ok(testSummary);
86
+ })()
87
+ );
88
+
89
+ when(get(`${storefrontURL}/plans`)).thenReturn(
90
+ (() => {
91
+ return ok([testPlan]);
92
+ })()
93
+ );
94
+
95
+ when(get(`${storefrontURL}/products`)).thenReturn(
96
+ (() => {
97
+ return ok([testProduct]);
98
+ })()
99
+ );
100
+
101
+ document.body.innerHTML = `
102
+ <div class="form-selector"></div>
103
+ <div class="summary-selector"></div>
104
+ `;
105
+
106
+ const options = {
107
+ form: '.form-selector',
108
+ summary: '.summary-selector',
109
+ _dev: {
110
+ framePayStyleLink: framePayStyleUrl
111
+ },
112
+ options: {
113
+ locale: 'auto',
114
+ intent: {
115
+ items: [
116
+ {
117
+ planId: 'test-plan-id-1',
118
+ quantity: 1
119
+ }
120
+ ]
121
+ }
122
+ }
123
+ };
124
+
125
+ const rebillyInstruments = MockRebillyInstruments({
126
+ theme: {
127
+ color: {
128
+ background: '#000'
129
+ }
130
+ },
131
+ css: `
132
+ .rebilly-instruments-summary-line-item-synopsis-title {
133
+ color: rgb(0, 68, 212);
134
+ }
135
+ `
136
+ });
137
+ await rebillyInstruments.mount(options);
138
+
139
+ // Mounts form and summary
140
+ const summarySelector = document.querySelector('.summary-selector');
141
+ expect(summarySelector.innerHTML).toMatch(testPlan.name);
142
+
143
+ // Theme config overrides initial styles
144
+ const SUMMARY_CONTAINER = summarySelector.querySelector(
145
+ '.rebilly-instruments-content'
146
+ );
147
+ expect(getComputedStyle(SUMMARY_CONTAINER).background).toEqual(
148
+ 'rgb(0, 0, 0)'
149
+ );
150
+
151
+ // CSS config property overrides initial styles
152
+ const LINE_ITEM_TITLE = document.querySelector(
153
+ '.rebilly-instruments-summary-line-item-synopsis-title'
154
+ );
155
+ expect(getComputedStyle(LINE_ITEM_TITLE).color).toEqual('rgb(0, 68, 212)');
156
+
157
+ // Mounts default FramePay script
158
+ const SCRIPTS = [...document.querySelectorAll('head script')];
159
+ const FRAMEPAY_SCRIPT = SCRIPTS.find(
160
+ (script) => script.src === framePayScriptUrl
161
+ );
162
+ expect(FRAMEPAY_SCRIPT.src).toEqual(framePayScriptUrl);
163
+
164
+ // Mounts _dev FramePay style
165
+ const STYLE_LINKS = [...document.querySelectorAll('head link')];
166
+ const FRAMEPAY_STYLE = STYLE_LINKS.find(
167
+ (script) => script.href === framePayStyleUrl
168
+ );
169
+ expect(FRAMEPAY_STYLE.href).toEqual(framePayStyleUrl);
170
+ });
171
+ });
@@ -1,19 +1,22 @@
1
- import Events from '../events';
1
+ import camelCase from 'lodash.camelcase';
2
+ import Events, { publicEventNames } from '../events';
3
+
4
+ /**
5
+ @typedef OnParams
6
+ @type {Object}
7
+ @property {string} eventName - The name of the event
8
+ @property {function} callback - The function that is triggered by the event.
9
+ */
2
10
 
3
11
  /**
4
12
  * Register events that will be triggered
5
- * @param {string} eventName - The name of the event
6
- * @param {function} callback - The function that is triggered by the event.
13
+ * @param {OnParams} params
7
14
  */
8
- export function On (eventName, callback) {
9
- switch(eventName) {
10
- case 'instrument-ready':
11
- Events.instrumentReady.addEventListener(callback);
12
- break;
13
- case 'purchase-complete':
14
- Events.purchaseComplete.addEventListener(callback);
15
- break;
16
- default:
17
- throw new Error(`${eventName} not a suported event`);
15
+ export function on({ eventName, callback }) {
16
+ if (!publicEventNames.includes(eventName)) {
17
+ throw new Error(`${eventName} is not a supported event`);
18
18
  }
19
- }
19
+
20
+ const internalEventName = camelCase(eventName);
21
+ Events[internalEventName].addEventListener(callback);
22
+ }
@@ -1,42 +1,52 @@
1
- import Events from '../events';
1
+ import camelCase from 'lodash.camelcase';
2
2
  import { MockRebillyInstruments } from 'tests/mocks/rebilly-instruments-mock';
3
+ import Events from '../events';
3
4
 
4
5
  describe('RebillyInstruments on', () => {
5
- it('should be able to register event listeners', () => {
6
- const events = [
7
- {
8
- name: 'instrument-ready',
9
- dispatch: Events.instrumentReady.dispatch
10
- },{
11
- name: 'purchase-complete',
12
- dispatch: Events.purchaseComplete.dispatch
13
- }
14
- ];
6
+ it('should register event listeners', async () => {
15
7
  const rebillyInstruments = MockRebillyInstruments();
8
+ const publicEventNames = ['instrument-ready', 'purchase-completed'];
9
+
10
+ await Promise.all(
11
+ publicEventNames.map(async (eventName) => {
12
+ const callback = jest.fn();
13
+ rebillyInstruments.on(eventName, callback);
14
+
15
+ const details = {
16
+ test: 'data'
17
+ };
18
+ Events[camelCase(eventName)].dispatch(details);
19
+
20
+ expect(callback).toBeCalledTimes(1);
21
+ expect(callback).toBeCalledWith(details);
22
+ })
23
+ );
24
+ });
16
25
 
26
+ it('should throw error for internal namespaced events', async () => {
27
+ const callback = jest.fn();
28
+ const rebillyInstruments = MockRebillyInstruments();
17
29
 
18
- events.forEach(async event => {
19
- const callback = jest.fn();
20
- rebillyInstruments.on(event.name, callback);
21
-
22
- const details = {
23
- test: 'data'
24
- };
25
- await event.dispatch(details);
26
-
27
- expect(callback).toBeCalledTimes(1);
28
- expect(callback).toBeCalledWith(details);
29
- });
30
-
31
-
30
+ let error;
31
+ try {
32
+ // rebilly-instruments-purchase-completed will be used internally but not available externally
33
+ await rebillyInstruments.on('rebilly-instruments-purchase-completed', callback);
34
+ } catch (e) {
35
+ error = e;
36
+ }
37
+ expect(error).toEqual(new Error('rebilly-instruments-purchase-completed is not a supported event'));
32
38
  });
33
39
 
34
- it('should throw error for a non defined event', () => {
40
+ it('should throw error for a non defined event', async () => {
35
41
  const callback = jest.fn();
36
42
  const rebillyInstruments = MockRebillyInstruments();
37
43
 
38
- expect(async () => {
39
- rebillyInstruments.on('not-an-event', callback);
40
- }).rejects.toEqual(new Error(`not-an-event not a suported event`));
44
+ let error;
45
+ try {
46
+ await rebillyInstruments.on('not-an-event', callback);
47
+ } catch (e) {
48
+ error = e;
49
+ }
50
+ expect(error).toEqual(new Error('not-an-event is not a supported event'));
41
51
  });
42
52
  });