@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.
- package/.babelrc +23 -5
- package/.eslintrc.js +27 -0
- package/.prettierrc.js +11 -0
- package/CONTRIBUTING.md +4 -0
- package/README.md +361 -2
- package/dist/events/base-event.js +51 -37
- package/dist/events/events.spec.js +18 -0
- package/dist/events/index.js +11 -8
- package/dist/functions/destroy.js +27 -5
- package/dist/functions/destroy.spec.js +69 -0
- package/dist/functions/initialize.js +60 -41
- package/dist/functions/initialize.spec.js +13 -13
- package/dist/functions/mount/fetch-summary-data.js +46 -0
- package/dist/functions/mount/fetch-summary-data.spec.js +44 -0
- package/dist/functions/mount/index.js +346 -0
- package/dist/functions/mount/mount.spec.js +135 -0
- package/dist/functions/on.js +26 -18
- package/dist/functions/on.spec.js +45 -63
- package/dist/functions/purchase.js +41 -154
- package/dist/functions/purchase.spec.js +60 -76
- package/dist/functions/show.js +39 -43
- package/dist/functions/show.spec.js +57 -0
- package/dist/functions/update.js +60 -5
- package/dist/functions/update.spec.js +100 -0
- package/dist/i18n/en.json +19 -0
- package/dist/i18n/es.json +19 -0
- package/dist/i18n/i18n.spec.js +6 -23
- package/dist/i18n/index.js +44 -67
- package/dist/index.js +70 -71
- package/dist/index.spec.js +24 -44
- package/dist/loader/index.js +63 -62
- package/dist/loader/loader.spec.js +14 -11
- package/dist/storefront/index.js +28 -39
- package/dist/storefront/models/plan-model.js +37 -54
- package/dist/storefront/models/product-model.js +25 -36
- package/dist/storefront/models/ready-to-pay-model.js +38 -42
- package/dist/storefront/models/summary-model.js +72 -99
- package/dist/storefront/plans.js +37 -84
- package/dist/storefront/plans.spec.js +61 -151
- package/dist/storefront/products.js +35 -83
- package/dist/storefront/products.spec.js +60 -150
- package/dist/storefront/purchase.js +27 -64
- package/dist/storefront/purchase.spec.js +51 -87
- package/dist/storefront/ready-to-pay.js +45 -107
- package/dist/storefront/ready-to-pay.spec.js +72 -147
- package/dist/storefront/storefront.spec.js +6 -6
- package/dist/storefront/summary.js +37 -84
- package/dist/storefront/summary.spec.js +135 -240
- package/dist/style/base/__snapshots__/theme.spec.js.snap +52 -0
- package/dist/style/base/index.js +72 -0
- package/dist/style/base/theme.js +73 -0
- package/dist/style/base/theme.spec.js +30 -0
- package/dist/style/browserslist.js +8 -0
- package/dist/style/components/address.js +64 -0
- package/dist/style/components/button.js +61 -0
- package/dist/style/components/divider.js +48 -0
- package/dist/style/components/forms/checkbox.js +83 -0
- package/dist/style/components/forms/field.js +53 -0
- package/dist/style/components/forms/form.js +28 -0
- package/dist/style/components/forms/input.js +45 -0
- package/dist/style/components/forms/label.js +43 -0
- package/dist/style/components/forms/select.js +63 -0
- package/dist/style/components/forms/validation.js +34 -0
- package/dist/style/components/icons.js +22 -0
- package/dist/style/components/index.js +57 -0
- package/dist/style/components/loader.js +48 -0
- package/dist/style/components/methods.js +104 -0
- package/dist/style/components/overlay.js +33 -0
- package/dist/style/helpers/index.js +59 -0
- package/dist/style/index.js +48 -0
- package/dist/style/payment-instruments/content.js +17 -0
- package/dist/style/payment-instruments/index.js +20 -0
- package/dist/style/payment-instruments/payment-card.js +35 -0
- package/dist/style/utils/color-values.js +22 -0
- package/dist/style/vendor/framepay.js +34 -0
- package/dist/style/vendor/postmate.js +17 -0
- package/dist/style/views/confirmation.js +85 -0
- package/dist/style/views/index.js +29 -0
- package/dist/style/views/method-selector.js +20 -0
- package/dist/style/views/modal.js +93 -0
- package/dist/style/views/result.js +61 -0
- package/dist/style/views/summary.js +123 -0
- package/dist/utils/add-dom-element.js +12 -34
- package/dist/utils/format-currency.js +4 -4
- package/dist/utils/has-valid-css-selector.js +2 -2
- package/dist/utils/index.js +15 -31
- package/dist/utils/is-dom-element.js +1 -1
- package/dist/utils/process-property-as-dom-element.js +12 -17
- package/dist/utils/sleep.js +10 -0
- package/{src/components → dist/views}/__snapshots__/summary.spec.js.snap +7 -3
- package/dist/views/common/iframe/base-iframe.js +57 -0
- package/dist/views/common/iframe/event-listeners.js +50 -0
- package/dist/views/common/iframe/index.js +19 -0
- package/dist/views/common/iframe/method-iframe.js +33 -0
- package/dist/views/common/iframe/modal-iframe.js +38 -0
- package/dist/views/common/iframe/view-iframe.js +31 -0
- package/dist/views/common/render-utilities.js +11 -0
- package/dist/views/confirmation.js +82 -0
- package/dist/views/method-selector/__snapshots__/method-selector.spec.js.snap +3 -0
- package/dist/views/method-selector/express-methods/apple-pay.js +92 -0
- package/dist/views/method-selector/express-methods/google-pay.js +32 -0
- package/dist/views/method-selector/express-methods/paypal.js +19 -0
- package/dist/views/method-selector/generate-digital-wallet.js +59 -0
- package/dist/views/method-selector/generate-digital-wallet.spec.js +132 -0
- package/dist/views/method-selector/get-method-data.js +25 -0
- package/dist/views/method-selector/get-payment-methods.js +55 -0
- package/dist/views/method-selector/get-payment-methods.spec.js +44 -0
- package/dist/views/method-selector/index.js +133 -0
- package/dist/views/method-selector/method-selector.spec.js +139 -0
- package/dist/views/method-selector/mount-express-methods.js +69 -0
- package/dist/views/method-selector/mount-methods.js +78 -0
- package/dist/views/modal.js +83 -0
- package/dist/views/result.js +42 -0
- package/dist/views/summary.js +162 -0
- package/dist/views/summary.spec.js +148 -0
- package/package.json +12 -6
- package/src/events/base-event.js +35 -12
- package/src/events/events.spec.js +11 -0
- package/src/events/index.js +12 -6
- package/src/functions/destroy.js +22 -3
- package/src/functions/destroy.spec.js +63 -0
- package/src/functions/initialize.js +43 -20
- package/src/functions/initialize.spec.js +9 -7
- package/src/functions/mount/fetch-summary-data.js +29 -0
- package/src/functions/mount/fetch-summary-data.spec.js +41 -0
- package/src/functions/mount/index.js +312 -0
- package/src/functions/mount/mount.spec.js +171 -0
- package/src/functions/on.js +17 -14
- package/src/functions/on.spec.js +39 -29
- package/src/functions/purchase.js +24 -64
- package/src/functions/purchase.spec.js +19 -17
- package/src/functions/show.js +27 -7
- package/src/functions/show.spec.js +61 -0
- package/src/functions/update.js +50 -3
- package/src/functions/update.spec.js +107 -0
- package/src/i18n/i18n.spec.js +6 -4
- package/src/i18n/index.js +20 -12
- package/src/index.js +43 -49
- package/src/index.spec.js +11 -42
- package/src/loader/index.js +55 -39
- package/src/loader/loader.spec.js +30 -23
- package/src/storefront/index.js +9 -7
- package/src/storefront/models/plan-model.js +1 -1
- package/src/storefront/models/product-model.js +1 -1
- package/src/storefront/models/ready-to-pay-model.js +10 -4
- package/src/storefront/models/summary-model.js +8 -15
- package/src/storefront/plans.js +16 -12
- package/src/storefront/plans.spec.js +29 -37
- package/src/storefront/products.js +16 -12
- package/src/storefront/products.spec.js +28 -39
- package/src/storefront/purchase.js +8 -6
- package/src/storefront/purchase.spec.js +18 -17
- package/src/storefront/ready-to-pay.js +19 -13
- package/src/storefront/ready-to-pay.spec.js +41 -41
- package/src/storefront/storefront.spec.js +1 -1
- package/src/storefront/summary.js +14 -12
- package/src/storefront/summary.spec.js +37 -50
- package/src/style/base/__snapshots__/theme.spec.js.snap +52 -0
- package/src/style/base/index.js +63 -0
- package/src/style/base/theme.js +61 -0
- package/src/style/base/theme.spec.js +32 -0
- package/src/style/browserslist.js +1 -0
- package/src/style/components/address.js +55 -0
- package/src/style/components/button.js +54 -0
- package/src/style/components/divider.js +39 -0
- package/src/style/components/forms/checkbox.js +76 -0
- package/src/style/components/forms/field.js +44 -0
- package/src/style/components/forms/form.js +19 -0
- package/src/style/components/forms/input.js +36 -0
- package/src/style/components/forms/label.js +34 -0
- package/src/style/components/forms/select.js +54 -0
- package/src/style/components/forms/validation.js +25 -0
- package/src/style/components/icons.js +13 -0
- package/src/style/components/index.js +35 -0
- package/src/style/components/loader.js +41 -0
- package/src/style/components/methods.js +93 -0
- package/src/style/components/overlay.js +24 -0
- package/src/style/helpers/index.js +51 -0
- package/src/style/index.js +30 -0
- package/src/style/payment-instruments/content.js +8 -0
- package/src/style/payment-instruments/index.js +10 -0
- package/src/style/payment-instruments/payment-card.js +26 -0
- package/src/style/utils/color-values.js +9 -0
- package/src/style/vendor/framepay.js +25 -0
- package/src/style/vendor/postmate.js +8 -0
- package/src/style/views/confirmation.js +76 -0
- package/src/style/views/index.js +16 -0
- package/src/style/views/method-selector.js +11 -0
- package/src/style/views/modal.js +84 -0
- package/src/style/views/result.js +52 -0
- package/src/style/views/summary.js +114 -0
- package/src/utils/add-dom-element.js +12 -13
- package/src/utils/format-currency.js +4 -1
- package/src/utils/has-valid-css-selector.js +2 -2
- package/src/utils/index.js +2 -6
- package/src/utils/is-dom-element.js +1 -1
- package/src/utils/process-property-as-dom-element.js +27 -24
- package/src/utils/sleep.js +3 -0
- package/src/views/__snapshots__/summary.spec.js.snap +292 -0
- package/src/views/common/iframe/base-iframe.js +46 -0
- package/src/views/common/iframe/event-listeners.js +27 -0
- package/src/views/common/iframe/index.js +7 -0
- package/src/views/common/iframe/method-iframe.js +21 -0
- package/src/views/common/iframe/modal-iframe.js +27 -0
- package/src/views/common/iframe/view-iframe.js +18 -0
- package/src/views/common/render-utilities.js +4 -0
- package/src/views/confirmation.js +57 -0
- package/src/views/method-selector/__snapshots__/method-selector.spec.js.snap +3 -0
- package/src/views/method-selector/express-methods/apple-pay.js +78 -0
- package/src/views/method-selector/express-methods/google-pay.js +25 -0
- package/src/views/method-selector/express-methods/paypal.js +7 -0
- package/src/views/method-selector/generate-digital-wallet.js +44 -0
- package/src/views/method-selector/generate-digital-wallet.spec.js +131 -0
- package/src/{components/form → views/method-selector}/get-method-data.js +9 -5
- package/src/views/method-selector/get-payment-methods.js +40 -0
- package/src/views/method-selector/get-payment-methods.spec.js +40 -0
- package/src/views/method-selector/index.js +110 -0
- package/src/views/method-selector/method-selector.spec.js +146 -0
- package/src/views/method-selector/mount-express-methods.js +53 -0
- package/src/views/method-selector/mount-methods.js +71 -0
- package/src/views/modal.js +84 -0
- package/src/views/result.js +30 -0
- package/src/{components → views}/summary.js +90 -21
- package/src/views/summary.spec.js +170 -0
- package/tests/async-utilities.js +22 -0
- package/tests/mocks/rebilly-instruments-mock.js +105 -7
- package/dist/components/confirmation.js +0 -103
- package/dist/components/form/form.js +0 -110
- package/dist/components/form/form.spec.js +0 -135
- package/dist/components/form/get-method-data.js +0 -21
- package/dist/components/form/get-payment-methods.js +0 -42
- package/dist/components/form/method-selector.js +0 -61
- package/dist/components/form/mount-express-payment-methods.js +0 -102
- package/dist/components/form/process-digital-wallet-options.js +0 -20
- package/dist/components/form/zoid-helpers.js +0 -130
- package/dist/components/result.js +0 -66
- package/dist/components/summary.js +0 -60
- package/dist/components/summary.spec.js +0 -144
- package/dist/events/instrument-ready.js +0 -51
- package/dist/events/purchase-complete.js +0 -51
- package/dist/functions/mount.js +0 -311
- package/dist/functions/mount.spec.js +0 -203
- package/dist/styles/base-styles.js +0 -12
- package/dist/styles/flat-theme-object.js +0 -42
- package/dist/styles/framepay.js +0 -15
- package/dist/styles/main.js +0 -25
- package/dist/styles/payment-card.js +0 -12
- package/dist/styles/shade-tint-values-helper.js +0 -28
- package/dist/styles/style-variables.js +0 -43
- package/dist/utils/camel-case.js +0 -12
- package/dist/utils/kebab-case.js +0 -10
- package/dist/utils/un-kebab-case.js +0 -10
- package/src/components/confirmation.js +0 -77
- package/src/components/form/__snapshots__/form.spec.js.snap +0 -43
- package/src/components/form/form.js +0 -88
- package/src/components/form/form.spec.js +0 -109
- package/src/components/form/get-payment-methods.js +0 -32
- package/src/components/form/method-selector.js +0 -47
- package/src/components/form/mount-express-payment-methods.js +0 -84
- package/src/components/form/process-digital-wallet-options.js +0 -11
- package/src/components/form/zoid-helpers.js +0 -114
- package/src/components/result.js +0 -50
- package/src/components/summary.spec.js +0 -106
- package/src/events/instrument-ready.js +0 -11
- package/src/events/purchase-complete.js +0 -11
- package/src/functions/mount.js +0 -204
- package/src/functions/mount.spec.js +0 -172
- package/src/styles/base-styles.js +0 -741
- package/src/styles/flat-theme-object.js +0 -12
- package/src/styles/framepay.js +0 -30
- package/src/styles/main.js +0 -17
- package/src/styles/payment-card.js +0 -18
- package/src/styles/shade-tint-values-helper.js +0 -13
- package/src/styles/style-variables.js +0 -34
- package/src/utils/camel-case.js +0 -3
- package/src/utils/kebab-case.js +0 -3
- 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
|
+
});
|
package/src/functions/on.js
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
|
-
import
|
|
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 {
|
|
6
|
-
* @param {function} callback - The function that is triggered by the event.
|
|
13
|
+
* @param {OnParams} params
|
|
7
14
|
*/
|
|
8
|
-
export function
|
|
9
|
-
|
|
10
|
-
|
|
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
|
+
}
|
package/src/functions/on.spec.js
CHANGED
|
@@ -1,42 +1,52 @@
|
|
|
1
|
-
import
|
|
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
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
});
|