@rebilly/instruments 2.0.0-beta → 3.0.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 (197) hide show
  1. package/dist/events/index.js +2 -1
  2. package/dist/functions/mount/fetch-data.js +187 -0
  3. package/dist/functions/mount/fetch-data.spec.js +189 -0
  4. package/dist/functions/mount/index.js +132 -164
  5. package/dist/functions/mount/mount.spec.js +2 -4
  6. package/dist/functions/mount/setup-element.js +40 -0
  7. package/dist/functions/mount/setup-framepay-theme.js +95 -0
  8. package/dist/functions/mount/setup-framepay.js +5 -1
  9. package/dist/functions/mount/setup-i18n.js +33 -0
  10. package/dist/functions/mount/setup-options.js +68 -47
  11. package/dist/functions/mount/setup-options.spec.js +66 -0
  12. package/dist/functions/mount/setup-storefront.js +6 -4
  13. package/dist/functions/mount/setup-styles.js +4 -2
  14. package/dist/functions/purchase.js +129 -24
  15. package/dist/functions/purchase.spec.js +13 -10
  16. package/dist/functions/setup.js +85 -0
  17. package/dist/functions/setup.spec.js +87 -0
  18. package/dist/functions/show.js +8 -4
  19. package/dist/functions/show.spec.js +9 -5
  20. package/dist/functions/update.js +39 -24
  21. package/dist/functions/update.spec.js +0 -4
  22. package/dist/i18n/en.json +5 -2
  23. package/dist/i18n/es.json +4 -1
  24. package/dist/index.js +17 -3
  25. package/dist/index.spec.js +3 -16
  26. package/dist/loader/index.js +4 -3
  27. package/dist/storefront/index.js +33 -0
  28. package/dist/storefront/invoices.js +27 -0
  29. package/dist/storefront/models/base-model.js +18 -0
  30. package/dist/storefront/models/invoice-model.js +14 -0
  31. package/dist/storefront/models/plan-model.js +4 -35
  32. package/dist/storefront/models/product-model.js +4 -23
  33. package/dist/storefront/models/summary-model.js +12 -25
  34. package/dist/storefront/models/transaction-model.js +31 -0
  35. package/dist/storefront/payment-instruments.js +47 -0
  36. package/dist/storefront/payment-instruments.spec.js +55 -0
  37. package/dist/storefront/plans.js +10 -18
  38. package/dist/storefront/plans.spec.js +3 -13
  39. package/dist/storefront/products.js +10 -13
  40. package/dist/storefront/products.spec.js +12 -19
  41. package/dist/storefront/purchase.js +23 -12
  42. package/dist/storefront/purchase.spec.js +1 -20
  43. package/dist/storefront/ready-to-pay.js +18 -15
  44. package/dist/storefront/ready-to-pay.spec.js +2 -12
  45. package/dist/storefront/summary.js +21 -17
  46. package/dist/storefront/summary.spec.js +4 -15
  47. package/dist/storefront/transactions.js +27 -0
  48. package/dist/style/base/__snapshots__/theme.spec.js.snap +188 -45
  49. package/dist/style/base/default-theme.js +699 -0
  50. package/dist/style/base/index.js +48 -16
  51. package/dist/style/base/theme.js +16 -48
  52. package/dist/style/base/theme.spec.js +4 -15
  53. package/dist/style/components/address.js +3 -3
  54. package/dist/style/components/button.js +32 -22
  55. package/dist/style/components/divider.js +9 -9
  56. package/dist/style/components/forms/checkbox.js +12 -9
  57. package/dist/style/components/forms/field.js +18 -6
  58. package/dist/style/components/forms/form.js +2 -2
  59. package/dist/style/components/forms/input.js +54 -13
  60. package/dist/style/components/forms/label.js +39 -18
  61. package/dist/style/components/forms/select.js +54 -22
  62. package/dist/style/components/forms/validation.js +53 -6
  63. package/dist/style/components/icons.js +4 -4
  64. package/dist/style/components/loader.js +5 -3
  65. package/dist/style/components/methods.js +18 -15
  66. package/dist/style/components/overlay.js +5 -5
  67. package/dist/style/helpers/index.js +46 -46
  68. package/dist/style/index.js +3 -1
  69. package/dist/style/payment-instruments/payment-card.js +4 -4
  70. package/dist/style/utils/border.js +47 -0
  71. package/dist/style/utils/color-values.js +39 -3
  72. package/dist/style/utils/remove-empty-null.js +20 -0
  73. package/dist/style/vendor/framepay.js +11 -8
  74. package/dist/style/vendor/postmate.js +2 -2
  75. package/dist/style/views/confirmation.js +13 -13
  76. package/dist/style/views/method-selector.js +2 -2
  77. package/dist/style/views/modal.js +6 -6
  78. package/dist/style/views/result.js +4 -4
  79. package/dist/style/views/summary.js +26 -22
  80. package/dist/views/__snapshots__/summary.spec.js.snap +77 -119
  81. package/dist/views/common/iframe/base-iframe.js +2 -0
  82. package/dist/views/common/iframe/modal-iframe.js +50 -4
  83. package/dist/views/confirmation.js +19 -8
  84. package/dist/views/method-selector/generate-digital-wallet.js +12 -3
  85. package/dist/views/method-selector/generate-digital-wallet.spec.js +11 -0
  86. package/dist/views/method-selector/get-payment-methods.js +13 -2
  87. package/dist/views/method-selector/get-payment-methods.spec.js +21 -19
  88. package/dist/views/method-selector/index.js +23 -34
  89. package/dist/views/method-selector/method-selector.spec.js +50 -55
  90. package/dist/views/method-selector/mount-methods.js +5 -8
  91. package/dist/views/modal.js +8 -2
  92. package/dist/views/result.js +3 -4
  93. package/dist/views/summary.js +156 -97
  94. package/dist/views/summary.spec.js +53 -58
  95. package/package.json +4 -2
  96. package/src/events/index.js +2 -1
  97. package/src/functions/mount/fetch-data.js +152 -0
  98. package/src/functions/mount/fetch-data.spec.js +238 -0
  99. package/src/functions/mount/index.js +101 -158
  100. package/src/functions/mount/mount.spec.js +3 -5
  101. package/src/functions/mount/setup-element.js +26 -0
  102. package/src/functions/mount/setup-framepay-theme.js +82 -0
  103. package/src/functions/mount/setup-framepay.js +5 -1
  104. package/src/functions/mount/setup-i18n.js +19 -0
  105. package/src/functions/mount/setup-options.js +78 -48
  106. package/src/functions/mount/setup-options.spec.js +60 -0
  107. package/src/functions/mount/setup-storefront.js +6 -4
  108. package/src/functions/mount/setup-styles.js +4 -2
  109. package/src/functions/on.spec.js +1 -1
  110. package/src/functions/purchase.js +99 -23
  111. package/src/functions/purchase.spec.js +10 -10
  112. package/src/functions/setup.js +48 -0
  113. package/src/functions/setup.spec.js +98 -0
  114. package/src/functions/show.js +2 -4
  115. package/src/functions/show.spec.js +3 -4
  116. package/src/functions/update.js +40 -25
  117. package/src/functions/update.spec.js +0 -4
  118. package/src/i18n/en.json +5 -2
  119. package/src/i18n/es.json +4 -1
  120. package/src/index.js +9 -3
  121. package/src/index.spec.js +3 -21
  122. package/src/loader/index.js +3 -3
  123. package/src/storefront/index.js +28 -0
  124. package/src/storefront/invoices.js +11 -0
  125. package/src/storefront/models/base-model.js +10 -0
  126. package/src/storefront/models/invoice-model.js +3 -0
  127. package/src/storefront/models/plan-model.js +3 -35
  128. package/src/storefront/models/product-model.js +3 -23
  129. package/src/storefront/models/summary-model.js +12 -19
  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 +6 -17
  134. package/src/storefront/plans.spec.js +4 -11
  135. package/src/storefront/products.js +8 -16
  136. package/src/storefront/products.spec.js +16 -22
  137. package/src/storefront/purchase.js +14 -16
  138. package/src/storefront/purchase.spec.js +2 -14
  139. package/src/storefront/ready-to-pay.js +13 -16
  140. package/src/storefront/ready-to-pay.spec.js +3 -10
  141. package/src/storefront/summary.js +19 -17
  142. package/src/storefront/summary.spec.js +5 -12
  143. package/src/storefront/transactions.js +11 -0
  144. package/src/style/base/__snapshots__/theme.spec.js.snap +188 -45
  145. package/src/style/base/default-theme.js +674 -0
  146. package/src/style/base/index.js +48 -16
  147. package/src/style/base/theme.js +17 -47
  148. package/src/style/base/theme.spec.js +4 -16
  149. package/src/style/components/address.js +3 -3
  150. package/src/style/components/button.js +32 -24
  151. package/src/style/components/divider.js +9 -9
  152. package/src/style/components/forms/checkbox.js +11 -11
  153. package/src/style/components/forms/field.js +18 -6
  154. package/src/style/components/forms/form.js +2 -2
  155. package/src/style/components/forms/input.js +54 -13
  156. package/src/style/components/forms/label.js +39 -18
  157. package/src/style/components/forms/select.js +54 -22
  158. package/src/style/components/forms/validation.js +53 -6
  159. package/src/style/components/icons.js +4 -4
  160. package/src/style/components/loader.js +4 -5
  161. package/src/style/components/methods.js +18 -15
  162. package/src/style/components/overlay.js +5 -5
  163. package/src/style/helpers/index.js +46 -46
  164. package/src/style/index.js +2 -1
  165. package/src/style/payment-instruments/payment-card.js +4 -4
  166. package/src/style/utils/border.js +34 -0
  167. package/src/style/utils/color-values.js +27 -1
  168. package/src/style/utils/remove-empty-null.js +10 -0
  169. package/src/style/vendor/framepay.js +11 -8
  170. package/src/style/vendor/postmate.js +2 -2
  171. package/src/style/views/confirmation.js +13 -13
  172. package/src/style/views/method-selector.js +2 -2
  173. package/src/style/views/modal.js +6 -6
  174. package/src/style/views/result.js +4 -4
  175. package/src/style/views/summary.js +26 -22
  176. package/src/views/__snapshots__/summary.spec.js.snap +77 -119
  177. package/src/views/common/iframe/base-iframe.js +2 -0
  178. package/src/views/common/iframe/modal-iframe.js +45 -3
  179. package/src/views/confirmation.js +15 -5
  180. package/src/views/method-selector/generate-digital-wallet.js +10 -3
  181. package/src/views/method-selector/generate-digital-wallet.spec.js +10 -0
  182. package/src/views/method-selector/get-payment-methods.js +7 -2
  183. package/src/views/method-selector/get-payment-methods.spec.js +26 -23
  184. package/src/views/method-selector/index.js +21 -28
  185. package/src/views/method-selector/method-selector.spec.js +49 -64
  186. package/src/views/method-selector/mount-methods.js +5 -8
  187. package/src/views/modal.js +6 -2
  188. package/src/views/result.js +4 -3
  189. package/src/views/summary.js +161 -117
  190. package/src/views/summary.spec.js +60 -75
  191. package/tests/mocks/rebilly-instruments-mock.js +37 -7
  192. package/tests/mocks/storefront-api-mock.js +8 -0
  193. package/tests/mocks/storefront-mock.js +17 -0
  194. package/dist/functions/mount/fetch-summary-data.js +0 -46
  195. package/dist/functions/mount/fetch-summary-data.spec.js +0 -43
  196. package/src/functions/mount/fetch-summary-data.js +0 -29
  197. package/src/functions/mount/fetch-summary-data.spec.js +0 -40
@@ -1,196 +1,129 @@
1
1
  /* eslint-disable max-len */
2
- import { collectData } from '@rebilly/risk-data-collector';
3
2
  import { mountSummary } from '../../views/summary';
4
3
  import { mountMethodSelector } from '../../views/method-selector';
5
- import { processPropertyAsDOMElement } from '../../utils';
6
- import { fetchSummaryData } from './fetch-summary-data';
4
+ import { fetchData } from './fetch-data';
7
5
  import { show } from '../show';
8
6
  import { on } from '../on';
7
+ import setupElement from './setup-element';
9
8
  import setupStorefront from './setup-storefront';
10
9
  import setupOptions from './setup-options';
11
10
  import setupFramepay from './setup-framepay';
12
11
  import setupStyles from './setup-styles';
12
+ import setupI18n from './setup-i18n';
13
+ import setupFramepayTheme from './setup-framepay-theme';
13
14
 
14
15
  /**
15
16
  * @typedef {object} Item
16
17
  * @property {string} planId - The Rebilly id of the plan.
17
18
  * @property {number} quantity - The number of the plans to be purchased.
18
19
  * @property {string} thumbnail - The source img for the product. Recommend 100px by 100px.
19
- */
20
-
21
- /**
22
- * @typedef {object} Intent
23
- * @property {"purchase" | "vault"} mode - Which mode the mount is usings. Default "purchase".
24
- * @property {Array.<Item>} items - Which plans the customer is purchasing.
25
- * @property {string} customerId - Which customer is associated with the instrument.
26
- * @property {string} countryCode - The country code for the transaction
27
- */
28
-
29
- /**
30
- * @typedef {GooglePayDisplayOptions} GooglePay
31
- * @param {"back" | "white"} buttonColor - default "black". Color of google pay button
32
- * @param {"short" | "long"} buttonType - default "short". The length of the button
33
- * @param {string} buttonHeight - The value and units of the button to match other payment buttons
34
- * <br>example: "44px", "1rem" etc.
35
- */
36
-
37
- /**
20
+ *
21
+ * @typedef {object} GooglePayDisplayOptions
22
+ * @property {"back" | "white"} [buttonColor=black] - Color of google pay button
23
+ * @property {"short" | "long"} [buttonType=short] - The length of the button
24
+ * @property {string} [buttonHeight=44px] - The value and units of the button to match other payment buttons
25
+ *
26
+ * @typedef {object} ApplePayDisplayOptions
27
+ * @property {"back" | "white"} [buttonColor=black] - Color of apple pay button
28
+ * @property {"short" | "long"} [buttonType=short] - The length of the button
29
+ * @property {string} [buttonHeight=44px] - The value and units of the button to match other payment buttons
30
+ *
31
+ * @typedef {object} ApplePayMerchantOptions
32
+ * @property {string} merchantName - The name of the merchant store.
33
+ *
38
34
  * @typedef {object} GooglePay
39
- * @param {GooglePayDisplayOptions} displayOptions - display options for google pay instrument
40
- */
41
-
42
- /**
35
+ * @property {GooglePayDisplayOptions} displayOptions - display options for google pay instrument
36
+ *
37
+ * @typedef {object} ApplePay
38
+ * @property {ApplePayDisplayOptions} displayOptions - display options for apple pay instrument
39
+ * @property {ApplePayMerchantOptions} merchantOptions - merchant options for apple pay instrument
40
+ *
43
41
  * @typedef {object} PaymentCard
44
- * @param {boolean} popup - default: false. Show method as a button with a form popup
45
- * <br>Otherwise the form will be mounted inline.
46
- */
47
-
48
- /**
42
+ * @property {boolean} [popup=true] - Show method as a button with a form popup. Otherwise the form will be mounted inline.
43
+ *
49
44
  * @typedef {object} Address
50
- * @param {string} name - default: default. One of default, combined, or stacked.
51
- * @param {string} region - default: default. One of default, split, or stacked.
52
- * @param {Array.<"organization" | "phoneNumber">} show. Show extra fields listed.
53
- * @param {Array.<"address" | "address2" | "email | "country" | "region" | "postalCode"">} hide. Hide the listed fields.
54
- * @param {Array.<"organization" | "address" | "address2" | "email" | "phoneNumber" | "country" | "region" | "postalCode">} require.
55
- * <br>If the field name is included, enforce the data for those inputs to be included.
56
- * <br>First name and last name are always required. Country is always required if the products require shipping.
57
- */
58
-
59
- /**
45
+ * @property {string} [name=default] - One of default, combined, or stacked.
46
+ * @property {string} [region=default] - One of default, split, or stacked.
47
+ * @property {Array.<"organization" | "phoneNumber">} show - Show extra fields listed.
48
+ * @property {Array.<"address" | "address2" | "email | "country" | "region" | "postalCode"">} hide - Hide the listed fields.
49
+ * @property {Array.<"organization" | "address" | "address2" | "email" | "phoneNumber" | "country" | "region" | "postalCode">} require - Require the listed fields.
50
+ *
60
51
  * @typedef {object} PaymentInstruments
61
- * @param {boolean} compactExpressInstruments - default: true. Show express methods as
62
- * <br>inline pill buttons, or list of full width button.
63
- * @param {PaymentCard} paymentCard - settings for payment card instruments
64
- * @param {GooglePay} googlePay - settings for google pay instruments
65
- * @param {Address} address - customization for address components for all payment instruments.
66
- */
67
-
68
- /**
52
+ * @property {boolean} [compactExpressInstruments=true] - Show express methods as inline pill buttons, or list of full width button.
53
+ * @property {PaymentCard} paymentCard - settings for payment card instruments
54
+ * @property {GooglePay} googlePay - settings for google pay instruments
55
+ * @property {ApplePay} applePay - settings for apple pay instruments
56
+ * @property {Address} address - customization for address components for all payment instruments.
57
+ *
69
58
  * @typedef {object} Features
70
- * @param {boolean} autoConfirmation - default: true. Will mount the confirmation screen after `instrument-ready`
71
- * <br>event is triggered. Will need to trigger purchase manually if set to false.
72
- * <br>Can use RebillyInstruments.show('confirmation', options) to mount defautl component
73
- * @param {boolean} autoResult - default: true. Show results of transaction after `purchase-completed` event is triggered
74
- * <br>Will need to handle purchase result manually if set to false.
75
- * <br>Can use RebillyInstruments.show('result', options) to display default component
76
- */
77
-
78
- /**
79
- * @typedef {object} Options
80
- * @property {Intent} intent - The information required for purchaseing or vaulting an instrument
81
- * @property {PaymentInstruments} paymentInstruments - settings for various payment instruments
82
- * @property {Features} features - flags to enable and disable different features
83
- * @property {string} locale - default: auto. Language to render component text
84
- */
85
-
86
- /**
87
- * Mount library with configurations.
88
- * @typedef {object} MountParams
89
- * @property {Object} state - Global state
90
- * @property {string | HTMLElement} form - The CSS class or HTML element were the form will be mounted.
91
- * @property {string | HTMLElement} form - The CSS class or HTML element were the form will be mounted.
92
- * @property {string | HTMLElement} summary - The CSS class or HTML element were the summary will be mounted.
93
- * @property {Options} options - The configurations that are to be passed to the library for use.
59
+ * @property {boolean} [autoConfirmation=true] - Will mount the confirmation screen after `instrument-ready` event is triggered.
60
+ * @property {boolean} [autoResult=true] - Show results of transaction after `purchase-completed` event is triggered
94
61
  */
95
62
 
96
63
  /**
97
64
  * Mount library with configurations.
98
- * @param {MountParams} params
65
+ * @param {object} options - The options object
66
+ * @param {object} options.state - Global state
67
+ * @param {string | HTMLElement} options.form - The CSS class or HTML element were the form will be mounted.
68
+ * @param {string | HTMLElement} options.summary - The CSS class or HTML element were the summary will be mounted.
69
+ * @param {Item[]} options.items - Which plans the customer is purchasing.
70
+ * @param {string} options.invoiceId - The Rebilly id of the invoice used for purchasing.
71
+ * @param {string} options.customerJwt - The customer token to access the invoice.
72
+ * @param {string} [options.countryCode=USD] - The country code for the transaction
73
+ * @param {PaymentInstruments} options.paymentInstruments - settings for various payment instruments
74
+ * @param {Features} options.features - flags to enable and disable different features
75
+ * @param {string} options.locale - default: auto. Language to render component text
99
76
  */
100
77
  export async function mount({
101
78
  state,
102
- form = '.rebilly-instruments',
103
- summary = '.rebilly-instruments-summary',
104
79
  ...options
105
80
  } = {}) {
106
-
107
- state.form = form;
108
- state.summary = summary;
109
- state.storefront = setupStorefront(options);
110
- state.options = setupOptions(options);
111
- state.mainStyle = await setupStyles(options);
112
- state._dev = options._dev;
113
-
114
- setupFramepay(options);
115
-
116
- state.form = processPropertyAsDOMElement({
117
- prop: state.form,
118
- propName: 'form'
119
- });
120
- state.summary = processPropertyAsDOMElement({
121
- prop: state.summary,
122
- propName: 'summary',
123
- isRequired: false
124
- });
125
-
126
- // Setup loader
127
- state.loader.addDOMElement({ el: state.form });
128
- state.loader.addDOMElement({ section: 'summary', el: state.summary });
129
-
130
81
  try {
82
+ state.data = {};
83
+ state.options = {};
84
+ // Setup DOM
85
+ state.form = setupElement({ element: 'form', options });
86
+ state.summary = setupElement({ element: 'summary', options });
87
+
88
+ // Setup loader
89
+ state.loader.addDOMElement({ el: state.form });
90
+ state.loader.addDOMElement({ section: 'summary', el: state.summary });
131
91
  state.loader.startLoading({ section: 'summary', id: 'initSummary' });
132
92
  state.loader.startLoading({ id: 'initForm' });
133
93
 
134
- const { riskMetadata } = await collectData();
135
-
136
- if (
137
- state.options.locale === 'auto' &&
138
- riskMetadata?.browserData?.language
139
- ) {
140
- const {
141
- browserData: { language }
142
- } = riskMetadata;
143
- state.options.locale = language;
94
+ // Setup state
95
+ state.storefront = setupStorefront({ options });
96
+ state.options = setupOptions({ options });
97
+ state.mainStyle = await setupStyles({ options });
98
+ state.data = await fetchData({ state });
99
+ state.options.themeFramepay = await setupFramepayTheme({ state, options });
100
+ const i18n = setupI18n({ state });
101
+ setupFramepay({ state });
102
+
103
+ if (state.data.transaction && state.data.transaction?.type === 'setup') {
104
+ state.options.transactionType = 'setup';
144
105
  }
145
106
 
146
- state.translate.init(state.options.locale, state.options.i18n);
147
-
148
- const {
149
- readyToPay,
150
- summary: summaryData,
151
- plans,
152
- products
153
- } = await fetchSummaryData({ riskMetadata, state });
154
-
155
- state.hasMounted = true;
156
-
107
+ // Mount content
157
108
  if (state.form) {
158
- const formOptions = {
159
- summary: summaryData,
160
- mainStyle: state.mainStyle,
161
- readyToPay,
162
- plans,
163
- products
164
- };
165
- mountMethodSelector({ state, formOptions });
109
+ mountMethodSelector({ state });
166
110
  }
167
-
168
111
  if (state.summary) {
169
- const summaryOptions = {
170
- summary: summaryData,
171
- readyToPay,
172
- plans,
173
- products
174
- };
175
- mountSummary({ state, summaryOptions });
112
+ mountSummary({ state });
176
113
  }
114
+ i18n({state});
115
+ state.hasMounted = true;
177
116
  } catch (error) {
178
117
  throw error;
179
118
  }
180
119
 
181
- state.translate.init(state.options.locale, state.options.i18n);
182
- state.translate.translateItems();
183
-
184
120
  if (state.options.features.autoConfirmation) {
185
121
  on({
186
122
  eventName: 'instrument-ready',
187
- callback: (instrument) => {
123
+ callback: (payload) => {
188
124
  show({
189
125
  componentName: 'confirmation',
190
- payload: {
191
- instrument,
192
- mainStyle: state.mainStyle
193
- },
126
+ payload,
194
127
  state
195
128
  });
196
129
  }
@@ -198,18 +131,28 @@ export async function mount({
198
131
  }
199
132
 
200
133
  if (state.options.features.autoResult) {
201
- on({
202
- eventName: 'purchase-completed',
203
- callback: (purchase) => {
204
- show({
205
- componentName: 'result',
206
- payload: {
207
- purchase,
208
- mainStyle: state.mainStyle
209
- },
210
- state
211
- });
212
- }
213
- });
134
+ if (state.options.transactionType === 'setup') {
135
+ on({
136
+ eventName: 'setup-completed',
137
+ callback: (payload) => {
138
+ show({
139
+ componentName: 'result',
140
+ payload,
141
+ state
142
+ });
143
+ }
144
+ });
145
+ } else {
146
+ on({
147
+ eventName: 'purchase-completed',
148
+ callback: (payload) => {
149
+ show({
150
+ componentName: 'result',
151
+ payload,
152
+ state
153
+ });
154
+ }
155
+ });
156
+ }
214
157
  }
215
158
  }
@@ -23,9 +23,7 @@ describe('RebillyInstruments instance', () => {
23
23
  }
24
24
  ],
25
25
  theme: {
26
- color: {
27
- background: '#000'
28
- }
26
+ colorBackground: '#000'
29
27
  },
30
28
  css: `
31
29
  .rebilly-instruments-summary-line-item-synopsis-title {
@@ -47,8 +45,8 @@ describe('RebillyInstruments instance', () => {
47
45
  const SUMMARY_CONTAINER = summarySelector.querySelector(
48
46
  '.rebilly-instruments-content'
49
47
  );
50
- expect(getComputedStyle(SUMMARY_CONTAINER).background).toEqual(
51
- 'rgb(0, 0, 0)'
48
+ expect(getComputedStyle(SUMMARY_CONTAINER).getPropertyValue('--rebilly-colorBackground')).toEqual(
49
+ '#000'
52
50
  );
53
51
 
54
52
  // CSS config property overrides initial styles
@@ -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
+ }
@@ -0,0 +1,82 @@
1
+ import css from 'css';
2
+ import postcss from 'postcss';
3
+ import postcssCustomProperties from 'postcss-custom-properties';
4
+ import camelCase from 'lodash.camelcase';
5
+
6
+ const resolveCssVars = async (rawCss) => postcss([
7
+ postcssCustomProperties({ preserve: false })
8
+ ])
9
+ .process(rawCss, { from: undefined })
10
+ .then(output => output.css);
11
+
12
+ const getStyleProps = (ast, selector) => {
13
+ const { rules } = ast.stylesheet;
14
+ const output = {
15
+ color: null,
16
+ fontFamily: null,
17
+ fontSize: null,
18
+ lineHeight: null,
19
+ fontWeight: null,
20
+ background: null
21
+ }
22
+
23
+ for(let i = 0; i < rules.length; i += 1) {
24
+ const rule = rules[i];
25
+
26
+ if(rule.type === 'rule' && rule.selectors.includes(selector)) {
27
+ rule.declarations.forEach(decl => {
28
+ const propName = camelCase(decl.property);
29
+ if(typeof output[propName] !== 'undefined') {
30
+ output[propName] = decl.value;
31
+ }
32
+ })
33
+ }
34
+ }
35
+ return output;
36
+ }
37
+
38
+ export default async ({
39
+ state,
40
+ options = {}
41
+ }) => {
42
+ const fullCss = `
43
+ ${state.mainStyle}
44
+ ${options.css || ''}
45
+ `;
46
+
47
+ const resolvedCss = await resolveCssVars(fullCss);
48
+ const cssAst = css.parse(resolvedCss);
49
+
50
+ const cssSelectors = {
51
+ base: '.rebilly-instruments-form-field-input',
52
+ baseHover: '.rebilly-instruments-form-field-input:hover',
53
+ baseFocus: '.rebilly-instruments-form-field-input:focus',
54
+ basePlaceholder: '.rebilly-instruments-form-field-input::placeholder',
55
+ baseSelection: '.rebilly-instruments-form-field-input::selection',
56
+
57
+ invalid: '.rebilly-instruments-form-field.is-error .rebilly-instruments-form-field-input',
58
+ invalidHover: '.rebilly-instruments-form-field.is-error .rebilly-instruments-form-field-input:hover',
59
+ invalidFocus: '.rebilly-instruments-form-field.is-error .rebilly-instruments-form-field-input:focus',
60
+ invalidPlaceholder: '.rebilly-instruments-form-field.is-error .rebilly-instruments-form-field-input::placeholder',
61
+ invalidSelection: '.rebilly-instruments-form-field.is-error .rebilly-instruments-form-field-input::selection'
62
+ }
63
+
64
+ const framepayStyle = {
65
+ base: {
66
+ ...getStyleProps(cssAst, cssSelectors.base),
67
+ ':hover': getStyleProps(cssAst, cssSelectors.baseHover),
68
+ ':focus': getStyleProps(cssAst, cssSelectors.baseFocus),
69
+ '::placeholder': getStyleProps(cssAst, cssSelectors.basePlaceholder),
70
+ '::selection': getStyleProps(cssAst, cssSelectors.baseSelection),
71
+ },
72
+ invalid: {
73
+ ...getStyleProps(cssAst, cssSelectors.invalid),
74
+ ':hover': getStyleProps(cssAst, cssSelectors.invalidHover),
75
+ ':focus': getStyleProps(cssAst, cssSelectors.invalidFocus),
76
+ '::placeholder': getStyleProps(cssAst, cssSelectors.invalidPlaceholder),
77
+ '::selection': getStyleProps(cssAst, cssSelectors.invalidSelection),
78
+ }
79
+ }
80
+
81
+ return framepayStyle;
82
+ }
@@ -1,7 +1,11 @@
1
1
  import { addDOMElement } from '../../utils';
2
2
 
3
3
  export default ({
4
- _dev
4
+ state: {
5
+ options: {
6
+ _dev
7
+ }
8
+ }
5
9
  } = {}) => {
6
10
  const framePayUrls = {
7
11
  script: _dev
@@ -0,0 +1,19 @@
1
+ const triggerTranslations = ({state}) => {
2
+ state.translate.init(state.options.locale, state.options.i18n);
3
+ state.translate.translateItems();
4
+ }
5
+
6
+ export default ({state = {}}) => {
7
+ if (
8
+ state.options.locale === 'auto' &&
9
+ state.data.riskMetadata?.browserData?.language
10
+ ) {
11
+ const {
12
+ browserData: { language }
13
+ } = state.data.riskMetadata;
14
+ state.options.locale = language;
15
+ }
16
+ state.translate.init(state.options.locale, state.options.i18n);
17
+
18
+ return triggerTranslations;
19
+ }
@@ -1,55 +1,75 @@
1
1
  import merge from 'lodash.merge';
2
2
 
3
- export default (options = {}) => {
4
- const {
5
- _dev
6
- } = options;
3
+ export const defaults = {
4
+ countryCode: 'US',
5
+ locale: 'auto',
6
+ theme: {
7
+ labels: 'floating'
8
+ },
9
+ paymentInstruments: {
10
+ address: {
11
+ name: 'default',
12
+ region: 'default',
13
+ hide: [],
14
+ show: [],
15
+ require: []
16
+ },
17
+ compactExpressInstruments: true,
18
+ googlePay: {
19
+ displayOptions: {
20
+ buttonColor: 'black',
21
+ buttonType: 'short',
22
+ buttonHeight: '44px'
23
+ }
24
+ },
25
+ applePay: {
26
+ displayOptions: {
27
+ buttonColor: 'black',
28
+ buttonType: 'plain',
29
+ buttonHeight: '44px'
30
+ }
31
+ },
32
+ paymentCard: {
33
+ popup: false
34
+ }
35
+ },
36
+ transactionType: 'purchase',
37
+ features: {
38
+ autoConfirmation: true,
39
+ autoResult: true
40
+ }
41
+ };
42
+
43
+ export function validateOptions(options) {
44
+ // TODO: validate more options
45
+ const purchaseData = [
46
+ options.items,
47
+ options.invoiceId,
48
+ options.money,
49
+ options.transactionId,
50
+ ].filter(v => v);
51
+
52
+ if (purchaseData.length === 0) {
53
+ throw new Error('Must provide purchase data');
54
+ }
7
55
 
8
- // TODO: validate options
56
+ if (purchaseData.length > 1) {
57
+ throw new Error('Must provide only one purchase data type');
58
+ }
59
+ }
60
+
61
+ export default ({
62
+ options = {}
63
+ } = {}) => {
64
+ validateOptions(options);
9
65
 
10
66
  const _computed = {
11
- paymentMethodsUrl: _dev
12
- ? _dev.paymentMethodsUrl || 'https://forms.local.rebilly.dev:3000'
67
+ paymentMethodsUrl: options._dev
68
+ ? options._dev.paymentMethodsUrl || 'https://forms.local.rebilly.dev:3000'
13
69
  : 'https://forms.secure-payments.app'
14
70
  };
15
71
 
16
- const defaults = {
17
- countryCode: 'US',
18
- locale: 'auto',
19
- paymentInstruments: {
20
- address: {
21
- name: 'default',
22
- region: 'default',
23
- hide: [],
24
- show: [],
25
- require: []
26
- },
27
- compactExpressInstruments: true,
28
- googlePay: {
29
- displayOptions: {
30
- buttonColor: 'black',
31
- buttonType: 'short',
32
- buttonHeight: '44px'
33
- }
34
- },
35
- applePay: {
36
- displayOptions: {
37
- buttonColor: 'black',
38
- buttonType: 'plain',
39
- buttonHeight: '44px'
40
- }
41
- },
42
- paymentCard: {
43
- popup: false
44
- }
45
- },
46
- features: {
47
- autoConfirmation: true,
48
- autoResult: true
49
- }
50
- };
51
-
52
- const combinedOptions = merge(defaults, {
72
+ const combinedOptions = merge({...defaults}, {
53
73
  organizationId: options.organizationId,
54
74
  publishableKey: options.publishableKey,
55
75
  websiteId: options.websiteId,
@@ -57,17 +77,27 @@ export default (options = {}) => {
57
77
  i18n: options.i18n,
58
78
  theme: options.theme,
59
79
  css: options.css,
60
- items: options.items,
61
80
  locale: options.locale,
62
81
  countryCode: options.countryCode,
63
82
  features: options.features,
64
83
  paymentInstruments: options.paymentInstruments,
84
+ transactionType: options.transactionType,
65
85
  _computed
66
86
  });
67
87
 
68
- if (_dev) {
69
- combinedOptions._dev = _dev;
70
- }
88
+ // Add optional key's
89
+ [
90
+ 'items',
91
+ 'money',
92
+ 'invoiceId',
93
+ 'transactionId',
94
+ 'customerJwt',
95
+ '_dev'
96
+ ].forEach(key => {
97
+ if (options[key]) {
98
+ combinedOptions[key] = options[key];
99
+ }
100
+ });
71
101
 
72
102
  return combinedOptions;
73
103
  }