@rebilly/instruments 3.15.4-beta.0 → 3.16.0-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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rebilly/instruments",
3
- "version": "3.15.4-beta.0",
3
+ "version": "3.16.0-beta.0",
4
4
  "author": "Rebilly",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -33,6 +33,7 @@
33
33
  "@rollup/plugin-replace": "^3.0.0",
34
34
  "babel-plugin-module-resolver": "^4.1.0",
35
35
  "component-emitter": "^1.3.0",
36
+ "core-js": "3.23.3",
36
37
  "jest": "^27.0.6",
37
38
  "msw": "0.38.2",
38
39
  "msw-when-then": "^1.5.1",
@@ -21,17 +21,20 @@ export class DataInstance {
21
21
  });
22
22
 
23
23
  this.money = state.options?.money || null;
24
+ this.couponIds = [];
24
25
  }
25
26
 
26
27
  get amountAndCurrency() {
27
28
  let currency;
28
29
  let amount;
30
+ let amountDue;
29
31
  if (this.previewPurchase) {
30
32
  currency = this.previewPurchase.currency;
31
33
  amount = this.previewPurchase.total;
32
34
  } else if (this.invoice) {
33
35
  currency = this.invoice.currency;
34
36
  amount = this.invoice.amount;
37
+ amountDue = this.invoice.amountDue;
35
38
  } else if (this.transaction) {
36
39
  currency = this.transaction.currency;
37
40
  amount = this.transaction.amount;
@@ -39,10 +42,14 @@ export class DataInstance {
39
42
  currency = this.money.currency;
40
43
  amount = this.money.amount;
41
44
  }
42
- return {
45
+ const amountAndCurrency = {
43
46
  amount,
44
47
  currency
48
+ };
49
+ if (amountDue) {
50
+ amountAndCurrency.amountDue = amountDue;
45
51
  }
52
+ return amountAndCurrency;
46
53
  }
47
54
 
48
55
  get isPayment() {
@@ -53,6 +60,13 @@ export class DataInstance {
53
60
  return this.previewPurchase;
54
61
  }
55
62
 
63
+ get hasAmountDue() {
64
+ return !!(
65
+ this.amountAndCurrency.amountDue &&
66
+ this.amountAndCurrency.amountDue !== this.amountAndCurrency.amount
67
+ );
68
+ }
69
+
56
70
  get summaryItems() {
57
71
  const {
58
72
  discountsAmount = null,
@@ -94,6 +108,7 @@ export class DataInstance {
94
108
  amountAndCurrency: this.amountAndCurrency,
95
109
  isPayment: this.isPayment,
96
110
  isPurchase: this.isPurchase,
111
+ hasAmountDue: this.hasAmountDue,
97
112
  summaryItems: this.summaryItems,
98
113
  summaryLineItems: this.summaryLineItems,
99
114
  isShippingRequired: this.isShippingRequired
@@ -186,4 +201,4 @@ export async function fetchData({
186
201
  }
187
202
 
188
203
  return new DataInstance({});
189
- }
204
+ }
@@ -40,7 +40,8 @@ export const defaults = {
40
40
  transactionType: 'purchase',
41
41
  features: {
42
42
  autoConfirmation: true,
43
- autoResult: true
43
+ autoResult: true,
44
+ showCoupons: null,
44
45
  }
45
46
  };
46
47
 
@@ -39,6 +39,10 @@ export async function makePayment({ state, payload }) {
39
39
  data.currency = state.options.money.currency;
40
40
  }
41
41
 
42
+ if (state.data.couponIds && Array.isArray(state.data.couponIds)) {
43
+ data.couponIds = state.data.couponIds
44
+ }
45
+
42
46
  let { fields } = await postPayment({
43
47
  state,
44
48
  data
@@ -57,16 +61,22 @@ export async function makePayment({ state, payload }) {
57
61
  }
58
62
 
59
63
  export async function makePurchase({ state, payload }) {
64
+ const data = {
65
+ websiteId: state.options.websiteId,
66
+ items: state.options.items,
67
+ paymentInstruction: {
68
+ token: payload._raw.id
69
+ },
70
+ ...payload
71
+ };
72
+
73
+ if (state.data.couponIds && Array.isArray(state.data.couponIds)) {
74
+ data.couponIds = state.data.couponIds
75
+ }
76
+
60
77
  const { fields } = await postPurchase({
61
78
  state,
62
- data: {
63
- websiteId: state.options.websiteId,
64
- items: state.options.items,
65
- paymentInstruction: {
66
- token: payload._raw.id
67
- },
68
- ...payload
69
- }
79
+ data
70
80
  });
71
81
  return fields;
72
82
  }
@@ -52,6 +52,7 @@ describe('RebillyInstruments purchase', () => {
52
52
  items: rebillyInstruments.state.options.items,
53
53
  billingAddress,
54
54
  deliveryAddress,
55
+ couponIds: [],
55
56
  paymentInstruction: {
56
57
  token: token.id
57
58
  }
@@ -60,7 +61,9 @@ describe('RebillyInstruments purchase', () => {
60
61
  await rebillyInstruments.purchase(purchasePayload);
61
62
 
62
63
  expect(spyStorefrontPurchase).toBeCalledTimes(1);
63
- expect(spyStorefrontPurchase).toBeCalledWith(expect.objectContaining({ data: purchasePayloadParsed }));
64
+ expect(spyStorefrontPurchase).toBeCalledWith({
65
+ data: expect.objectContaining(purchasePayloadParsed)
66
+ });
64
67
 
65
68
  expect(Events.purchaseCompleted.dispatch).toBeCalledTimes(1);
66
69
 
@@ -13,6 +13,11 @@ export async function fetchSummary({ data = null, state = null } = {}) {
13
13
 
14
14
  if (state.options?.items) {
15
15
  payload.data.items = state.options.items;
16
+ } else {
17
+ payload.data.items = state.data.summaryLineItems.map(item => ({
18
+ planId: item.planId,
19
+ quantity: item.quantity,
20
+ }));
16
21
  }
17
22
 
18
23
  if (state.data?.amountAndCurrency) {
@@ -29,6 +34,10 @@ export async function fetchSummary({ data = null, state = null } = {}) {
29
34
  payload.data.deliveryAddress = data.deliveryAddress;
30
35
  }
31
36
 
37
+ if(state.data?.couponIds) {
38
+ payload.data.couponIds = state.data.couponIds;
39
+ }
40
+
32
41
  const { fields: summaryFields } = await state.storefront.purchase.preview(
33
42
  payload
34
43
  );
@@ -40,10 +40,10 @@ describe('Storefront API Summary', () => {
40
40
 
41
41
  expect(instance.storefront.purchase.preview).toBeCalledTimes(1);
42
42
  expect(instance.storefront.purchase.preview).toBeCalledWith({
43
- data: {
43
+ data: expect.objectContaining({
44
44
  items: options.items,
45
45
  websiteId: options.websiteId
46
- }
46
+ })
47
47
  });
48
48
  expect(response).toBeInstanceOf(SummaryModel);
49
49
  expect(response).toEqual(new SummaryModel(testSummary));
@@ -78,11 +78,11 @@ describe('Storefront API Summary', () => {
78
78
 
79
79
  expect(instance.storefront.purchase.preview).toBeCalledTimes(1);
80
80
  expect(instance.storefront.purchase.preview).toBeCalledWith({
81
- data: {
81
+ data: expect.objectContaining({
82
82
  items: options.items,
83
83
  websiteId: options.websiteId,
84
84
  billingAddress
85
- }
85
+ })
86
86
  });
87
87
  });
88
88
 
@@ -115,11 +115,11 @@ describe('Storefront API Summary', () => {
115
115
 
116
116
  expect(instance.storefront.purchase.preview).toBeCalledTimes(1);
117
117
  expect(instance.storefront.purchase.preview).toBeCalledWith({
118
- data: {
118
+ data: expect.objectContaining({
119
119
  items: options.items,
120
120
  websiteId: options.websiteId,
121
121
  deliveryAddress
122
- }
122
+ })
123
123
  });
124
124
  });
125
125
 
@@ -0,0 +1,6 @@
1
+ export function changeIframeSrcHandler(iframe) {
2
+ iframe.component.on(`${iframe.name}-change-iframe-src`, (url = null) => {
3
+ iframe.component.frame.src = url;
4
+ iframe.component.frame.style.height = '75vh';
5
+ });
6
+ }
@@ -0,0 +1,8 @@
1
+ import camelCase from 'lodash.camelcase';
2
+ import Events from '../../../../events';
3
+
4
+ export function dispatchEventHandler(iframe) {
5
+ iframe.component.on(`${iframe.name}-dispatch`, ({ event, detail }) => {
6
+ Events[camelCase(event).replace(/-/, '')].dispatch(detail);
7
+ });
8
+ }
@@ -0,0 +1,9 @@
1
+ export function resizeComponentHandler(iframe) {
2
+ let prevHeight = '';
3
+ iframe.component.on(`${iframe.name}-resize-frame`, (height) => {
4
+ if (height !== prevHeight) {
5
+ prevHeight = height;
6
+ iframe.component.frame.style.height = height;
7
+ }
8
+ });
9
+ }
@@ -0,0 +1,5 @@
1
+ import { showError } from '../../../errors';
2
+
3
+ export function showErrorHandler(iframe) {
4
+ iframe.component.on(`${iframe.name}-show-error`, showError);
5
+ }
@@ -0,0 +1,9 @@
1
+ export function stopLoaderHandler(iframe, data) {
2
+ iframe.component.on(`${iframe.name}-stop-loading`, (id) => {
3
+ let {section} = data;
4
+ if (!section) {
5
+ section = id.includes('summary') ? 'summary' : 'form';
6
+ }
7
+ data.loader?.stopLoading({ section, id });
8
+ });
9
+ }
@@ -0,0 +1,18 @@
1
+ export function updateCouponsHandler(iframe) {
2
+ iframe.component.on(`${iframe.name}-update-coupon`, async ({
3
+ couponIds,
4
+ previewPurchase
5
+ } = {}) => {
6
+ iframe.state.data.couponIds = couponIds;
7
+ iframe.state.data.previewPurchase = previewPurchase;
8
+
9
+ const updateModel = {
10
+ data: iframe.state.data.toPostmatesModel(),
11
+ options: iframe.state.options
12
+ }
13
+ if (iframe.state.iframeComponents.summary) {
14
+ iframe.state.iframeComponents.summary.component.call('update', updateModel);
15
+ }
16
+ iframe.state.iframeComponents.form.component.call('update', updateModel);
17
+ });
18
+ }
@@ -1,11 +1,10 @@
1
1
  import BaseIframe from './base-iframe';
2
- import {
3
- dispatchRebillyInsturmentEventHandler,
4
- resizeComponentHandler,
5
- changeIframeSrcHandler,
6
- stopLoaderHandler,
7
- showErrorHandler
8
- } from './event-listeners';
2
+
3
+ import { changeIframeSrcHandler } from './events/change-iframe-src-handler';
4
+ import { resizeComponentHandler } from './events/resize-component-handler';
5
+ import { dispatchEventHandler } from './events/dispatch-event-handler';
6
+ import { showErrorHandler } from './events/show-error-handler';
7
+ import { stopLoaderHandler } from './events/stop-loader-handler';
9
8
 
10
9
  export class ModalIframe extends BaseIframe {
11
10
  constructor(args = {}) {
@@ -13,7 +12,7 @@ export class ModalIframe extends BaseIframe {
13
12
  }
14
13
 
15
14
  bindEventListeners({ close = () => {}, loader } = {}) {
16
- dispatchRebillyInsturmentEventHandler(this);
15
+ dispatchEventHandler(this);
17
16
  resizeComponentHandler(this);
18
17
  changeIframeSrcHandler(this);
19
18
  stopLoaderHandler(this, {
@@ -28,7 +27,7 @@ export class ModalIframe extends BaseIframe {
28
27
  close(...args);
29
28
  });
30
29
 
31
- // Close modal via postMessage (specifically during approal url flow)
30
+ // Close modal via postMessage (specifically during approval url flow)
32
31
  window.addEventListener('message', async (event) => {
33
32
  if(event.data === 'rebilly-instruments-approval-url-close') {
34
33
  if (this.state.options.transactionType === 'purchase') {
@@ -1,10 +1,10 @@
1
1
  import BaseIframe from './base-iframe';
2
- import {
3
- dispatchRebillyInsturmentEventHandler,
4
- resizeComponentHandler,
5
- stopLoaderHandler,
6
- showErrorHandler,
7
- } from './event-listeners';
2
+
3
+ import { resizeComponentHandler } from './events/resize-component-handler';
4
+ import { dispatchEventHandler } from './events/dispatch-event-handler';
5
+ import { updateCouponsHandler } from './events/update-coupons-handler';
6
+ import { showErrorHandler } from './events/show-error-handler';
7
+ import { stopLoaderHandler } from './events/stop-loader-handler';
8
8
 
9
9
  export class ViewIframe extends BaseIframe {
10
10
  constructor(args = {}) {
@@ -12,9 +12,10 @@ export class ViewIframe extends BaseIframe {
12
12
  }
13
13
 
14
14
  bindEventListeners({ loader } = {}) {
15
- dispatchRebillyInsturmentEventHandler(this);
15
+ dispatchEventHandler(this);
16
16
  resizeComponentHandler(this);
17
17
  stopLoaderHandler(this, { loader });
18
18
  showErrorHandler(this);
19
+ updateCouponsHandler(this);
19
20
  }
20
21
  }
@@ -68,6 +68,7 @@ export async function mountMethodSelector({ state }) {
68
68
 
69
69
  const name = 'rebilly-instruments-form';
70
70
  const iframe = await new ViewIframe({
71
+ state,
71
72
  name,
72
73
  url: `${paymentMethodsUrl}`,
73
74
  container: METHODS_CONTAINER,
@@ -18,6 +18,7 @@ export async function mountResult({ payload, state }) {
18
18
  };
19
19
 
20
20
  const iframe = await new ViewIframe({
21
+ state,
21
22
  name: 'rebilly-instruments-result',
22
23
  url: `${paymentMethodsUrl}/result`,
23
24
  container,
@@ -10,6 +10,7 @@ export async function mountSummary({ state }) {
10
10
  const { paymentMethodsUrl } = state.options._computed;
11
11
 
12
12
  const iframe = await new ViewIframe({
13
+ state,
13
14
  name: 'rebilly-instruments-summary',
14
15
  url: `${paymentMethodsUrl}/summary`,
15
16
  container: state.summary,
@@ -1,50 +0,0 @@
1
- import camelCase from 'lodash.camelcase';
2
- import Events from '../../../events';
3
- import { showError } from '../../errors';
4
-
5
- export function dispatchRebillyInsturmentEventHandler(iframe) {
6
- iframe.component.on(`${iframe.name}-dispatch`, ({ event, detail }) => {
7
- Events[camelCase(event).replace(/-/, '')].dispatch(detail);
8
- });
9
- }
10
-
11
- export function resizeComponentHandler(iframe) {
12
- let prevHeight = '';
13
- iframe.component.on(`${iframe.name}-resize-frame`, (height) => {
14
- if (height !== prevHeight) {
15
- prevHeight = height;
16
- iframe.component.frame.style.height = height;
17
- }
18
- });
19
- }
20
-
21
- export function stopLoaderHandler(iframe, data) {
22
- iframe.component.on(`${iframe.name}-stop-loading`, (id) => {
23
- let {section} = data;
24
- if (!section) {
25
- section = id.includes('summary') ? 'summary' : 'form';
26
- }
27
- data.loader?.stopLoading({ section, id });
28
- });
29
- }
30
-
31
- export function changeIframeSrcHandler(iframe) {
32
- iframe.component.on(`${iframe.name}-change-iframe-src`, (url = null) => {
33
- iframe.component.frame.src = url;
34
- iframe.component.frame.style.height = '75vh';
35
- });
36
- }
37
-
38
- export function displayOverlay(iframe) {
39
- iframe.component.on(`${iframe.name}-change-overlay`, (showOverlay = true) => {
40
- if (showOverlay) {
41
- iframe.component.frame.classList.add('rebilly-instruments-iframe-overlay');
42
- } else {
43
- iframe.component.frame.classList.remove('rebilly-instruments-iframe-overlay');
44
- }
45
- });
46
- }
47
-
48
- export function showErrorHandler(iframe) {
49
- iframe.component.on(`${iframe.name}-show-error`, showError);
50
- }