@internetarchive/donation-form 0.5.8 → 0.5.9-alpha.1

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 (53) hide show
  1. package/dist/src/braintree-manager/braintree-interfaces.d.ts +6 -0
  2. package/dist/src/braintree-manager/braintree-interfaces.js.map +1 -1
  3. package/dist/src/braintree-manager/braintree-manager.d.ts +4 -1
  4. package/dist/src/braintree-manager/braintree-manager.js +11 -0
  5. package/dist/src/braintree-manager/braintree-manager.js.map +1 -1
  6. package/dist/src/braintree-manager/payment-providers/credit-card/credit-card-interface.d.ts +6 -0
  7. package/dist/src/braintree-manager/payment-providers/credit-card/credit-card-interface.js.map +1 -1
  8. package/dist/src/braintree-manager/payment-providers/credit-card/credit-card.d.ts +12 -2
  9. package/dist/src/braintree-manager/payment-providers/credit-card/credit-card.js +60 -8
  10. package/dist/src/braintree-manager/payment-providers/credit-card/credit-card.js.map +1 -1
  11. package/dist/src/braintree-manager/payment-providers/credit-card/hosted-field-container.d.ts +18 -5
  12. package/dist/src/braintree-manager/payment-providers/credit-card/hosted-field-container.js +15 -0
  13. package/dist/src/braintree-manager/payment-providers/credit-card/hosted-field-container.js.map +1 -1
  14. package/dist/src/braintree-manager/payment-providers-interface.d.ts +4 -1
  15. package/dist/src/braintree-manager/payment-providers-interface.js.map +1 -1
  16. package/dist/src/braintree-manager/payment-providers.d.ts +4 -1
  17. package/dist/src/braintree-manager/payment-providers.js +13 -1
  18. package/dist/src/braintree-manager/payment-providers.js.map +1 -1
  19. package/dist/src/donation-form-controller.js +12 -0
  20. package/dist/src/donation-form-controller.js.map +1 -1
  21. package/dist/src/util/promisedSleep.d.ts +1 -0
  22. package/dist/src/util/promisedSleep.js +4 -0
  23. package/dist/src/util/promisedSleep.js.map +1 -0
  24. package/dist/test/mocks/mock-braintree-manager.d.ts +4 -1
  25. package/dist/test/mocks/mock-braintree-manager.js +5 -0
  26. package/dist/test/mocks/mock-braintree-manager.js.map +1 -1
  27. package/dist/test/mocks/mock-hosted-fields-container.d.ts +2 -0
  28. package/dist/test/mocks/mock-hosted-fields-container.js +6 -0
  29. package/dist/test/mocks/mock-hosted-fields-container.js.map +1 -1
  30. package/dist/test/mocks/payment-providers/individual-providers/mock-creditcard-handler.d.ts +4 -1
  31. package/dist/test/mocks/payment-providers/individual-providers/mock-creditcard-handler.js +5 -0
  32. package/dist/test/mocks/payment-providers/individual-providers/mock-creditcard-handler.js.map +1 -1
  33. package/dist/test/mocks/payment-providers/mock-payment-providers.d.ts +4 -1
  34. package/dist/test/mocks/payment-providers/mock-payment-providers.js +5 -0
  35. package/dist/test/mocks/payment-providers/mock-payment-providers.js.map +1 -1
  36. package/dist/test/tests/donation-form-controller.test.js +1 -1
  37. package/dist/test/tests/donation-form-controller.test.js.map +1 -1
  38. package/dist/test/tests/donation-form.test.js +1 -1
  39. package/dist/test/tests/donation-form.test.js.map +1 -1
  40. package/dist/test/tests/form-elements/payment-selector.test.js +1 -1
  41. package/dist/test/tests/form-elements/payment-selector.test.js.map +1 -1
  42. package/dist/test/tests/payment-providers/creditcard.test.js +101 -0
  43. package/dist/test/tests/payment-providers/creditcard.test.js.map +1 -1
  44. package/package.json +2 -1
  45. package/src/braintree-manager/braintree-interfaces.ts +11 -0
  46. package/src/braintree-manager/braintree-manager.ts +19 -0
  47. package/src/braintree-manager/payment-providers/credit-card/credit-card-interface.ts +10 -0
  48. package/src/braintree-manager/payment-providers/credit-card/credit-card.ts +79 -12
  49. package/src/braintree-manager/payment-providers/credit-card/hosted-field-container.ts +35 -8
  50. package/src/braintree-manager/payment-providers-interface.ts +12 -1
  51. package/src/braintree-manager/payment-providers.ts +22 -2
  52. package/src/donation-form-controller.ts +17 -3
  53. package/src/util/promisedSleep.ts +3 -0
@@ -1 +1 @@
1
- {"version":3,"file":"payment-selector.test.js","sourceRoot":"","sources":["../../../../test/tests/form-elements/payment-selector.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, fixture, expect, elementUpdated, oneEvent } from '@open-wc/testing';\nimport { PaymentSelector } from '../../../src/form-elements/payment-selector';\nimport '../../../src/form-elements/payment-selector';\nimport { MockPaymentProviders } from '../../mocks/payment-providers/mock-payment-providers';\nimport { promisedSleep } from '../../helpers/promisedSleep';\n\ndescribe('Payment Selector', () => {\n it('shows Venmo if it is available', async () => {\n const el = (await fixture(html`\n <payment-selector></payment-selector>\n `)) as PaymentSelector;\n\n const paymentProviders = new MockPaymentProviders();\n el.paymentProviders = paymentProviders;\n await elementUpdated(el);\n await promisedSleep(250);\n\n const venmoButton = el.shadowRoot?.querySelector('.venmo.provider-button');\n expect(venmoButton?.classList.contains('available')).to.be.true;\n });\n\n it('can show PayPal when called', async () => {\n const el = (await fixture(html`\n <payment-selector></payment-selector>\n `)) as PaymentSelector;\n el.showPaypalButton();\n await elementUpdated(el);\n const paypalButton = el.shadowRoot?.querySelector('.paypal-container.provider-button');\n expect(paypalButton?.classList.contains('available')).to.be.true;\n });\n\n it('emits paypalBlockerSelected event when paypal is selected in an error state', async () => {\n const el = (await fixture(html`\n <payment-selector></payment-selector>\n `)) as PaymentSelector;\n const paypalBlocker = el.shadowRoot?.querySelector('.paypal-local-button');\n const clickEvent = new MouseEvent('click');\n setTimeout(() => {\n paypalBlocker?.dispatchEvent(clickEvent);\n });\n const response = await oneEvent(el, 'paypalBlockerSelected');\n expect(response).to.exist;\n });\n\n it('emits applePaySelected event with original click event when ApplePay is selected', async () => {\n const el = (await fixture(html`\n <payment-selector></payment-selector>\n `)) as PaymentSelector;\n const paypalBlocker = el.shadowRoot?.querySelector('.applepay.provider-button');\n const clickEvent = new MouseEvent('click');\n setTimeout(() => {\n paypalBlocker?.dispatchEvent(clickEvent);\n });\n const response = await oneEvent(el, 'applePaySelected');\n const event = response.detail.originalEvent;\n expect(event).to.equal(clickEvent);\n });\n\n it('emits googlePaySelected when GooglePay is selected', async () => {\n const el = (await fixture(html`\n <payment-selector></payment-selector>\n `)) as PaymentSelector;\n const paypalBlocker = el.shadowRoot?.querySelector('.googlepay.provider-button');\n const clickEvent = new MouseEvent('click');\n setTimeout(() => {\n paypalBlocker?.dispatchEvent(clickEvent);\n });\n const response = await oneEvent(el, 'googlePaySelected');\n expect(response).to.exist;\n });\n});\n"]}
1
+ {"version":3,"file":"payment-selector.test.js","sourceRoot":"","sources":["../../../../test/tests/form-elements/payment-selector.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, fixture, expect, elementUpdated, oneEvent } from '@open-wc/testing';\nimport { PaymentSelector } from '../../../src/form-elements/payment-selector';\nimport '../../../src/form-elements/payment-selector';\nimport { MockPaymentProviders } from '../../mocks/payment-providers/mock-payment-providers';\nimport { promisedSleep } from '../../../src/util/promisedSleep';\n\ndescribe('Payment Selector', () => {\n it('shows Venmo if it is available', async () => {\n const el = (await fixture(html`\n <payment-selector></payment-selector>\n `)) as PaymentSelector;\n\n const paymentProviders = new MockPaymentProviders();\n el.paymentProviders = paymentProviders;\n await elementUpdated(el);\n await promisedSleep(250);\n\n const venmoButton = el.shadowRoot?.querySelector('.venmo.provider-button');\n expect(venmoButton?.classList.contains('available')).to.be.true;\n });\n\n it('can show PayPal when called', async () => {\n const el = (await fixture(html`\n <payment-selector></payment-selector>\n `)) as PaymentSelector;\n el.showPaypalButton();\n await elementUpdated(el);\n const paypalButton = el.shadowRoot?.querySelector('.paypal-container.provider-button');\n expect(paypalButton?.classList.contains('available')).to.be.true;\n });\n\n it('emits paypalBlockerSelected event when paypal is selected in an error state', async () => {\n const el = (await fixture(html`\n <payment-selector></payment-selector>\n `)) as PaymentSelector;\n const paypalBlocker = el.shadowRoot?.querySelector('.paypal-local-button');\n const clickEvent = new MouseEvent('click');\n setTimeout(() => {\n paypalBlocker?.dispatchEvent(clickEvent);\n });\n const response = await oneEvent(el, 'paypalBlockerSelected');\n expect(response).to.exist;\n });\n\n it('emits applePaySelected event with original click event when ApplePay is selected', async () => {\n const el = (await fixture(html`\n <payment-selector></payment-selector>\n `)) as PaymentSelector;\n const paypalBlocker = el.shadowRoot?.querySelector('.applepay.provider-button');\n const clickEvent = new MouseEvent('click');\n setTimeout(() => {\n paypalBlocker?.dispatchEvent(clickEvent);\n });\n const response = await oneEvent(el, 'applePaySelected');\n const event = response.detail.originalEvent;\n expect(event).to.equal(clickEvent);\n });\n\n it('emits googlePaySelected when GooglePay is selected', async () => {\n const el = (await fixture(html`\n <payment-selector></payment-selector>\n `)) as PaymentSelector;\n const paypalBlocker = el.shadowRoot?.querySelector('.googlepay.provider-button');\n const clickEvent = new MouseEvent('click');\n setTimeout(() => {\n paypalBlocker?.dispatchEvent(clickEvent);\n });\n const response = await oneEvent(el, 'googlePaySelected');\n expect(response).to.exist;\n });\n});\n"]}
@@ -6,8 +6,13 @@ import { mockHostedFieldTokenizePayload } from '../../mocks/payment-clients/mock
6
6
  import { CreditCardHandler } from '../../../src/braintree-manager/payment-providers/credit-card/credit-card';
7
7
  import { MockHostedFieldContainer } from '../../mocks/mock-hosted-fields-container';
8
8
  import { HostedFieldConfiguration } from '../../../src/braintree-manager/payment-providers/credit-card/hosted-field-configuration';
9
+ import Sinon from 'sinon';
9
10
  import { mockHostedFieldStyle, mockHostedFieldFieldOptions, mockHostedFieldConfig, } from '../../mocks/mock-hosted-fields-config';
11
+ const sandbox = Sinon.createSandbox();
10
12
  describe('CreditCardHandler', () => {
13
+ afterEach(() => {
14
+ sandbox.restore();
15
+ });
11
16
  it('can tokenize the hosted fields', () => __awaiter(void 0, void 0, void 0, function* () {
12
17
  const braintreeManager = new MockBraintreeManager();
13
18
  const client = new MockHostedFieldsClient({
@@ -89,5 +94,101 @@ describe('CreditCardHandler', () => {
89
94
  handler.hideErrorMessage();
90
95
  expect(mockHostedFieldContainer.hideErrorMessageCalled).to.be.true;
91
96
  }));
97
+ it('retries the expected number of times before failure', () => __awaiter(void 0, void 0, void 0, function* () {
98
+ const braintreeManager = new MockBraintreeManager();
99
+ const client = new MockHostedFieldsClient();
100
+ let retryCount = 0;
101
+ Sinon.stub(client, 'create').callsFake(() => {
102
+ retryCount++;
103
+ throw new Error('Error');
104
+ });
105
+ const mockHostedFieldContainer = new MockHostedFieldContainer();
106
+ const hostedFieldsSpy = Sinon.spy(mockHostedFieldContainer, 'resetHostedFields');
107
+ const mockHostedFieldConfig = new HostedFieldConfiguration({
108
+ hostedFieldStyle: mockHostedFieldStyle,
109
+ hostedFieldFieldOptions: mockHostedFieldFieldOptions,
110
+ hostedFieldContainer: mockHostedFieldContainer,
111
+ });
112
+ const handler = new CreditCardHandler({
113
+ braintreeManager: braintreeManager,
114
+ hostedFieldClient: client,
115
+ hostedFieldConfig: mockHostedFieldConfig,
116
+ retryInverval: 0.01,
117
+ maxRetryCount: 3,
118
+ loadTimeout: 0.01,
119
+ });
120
+ try {
121
+ yield handler.instance.get();
122
+ expect.fail('Should have thrown an error');
123
+ }
124
+ catch (e) { }
125
+ // initial call + 3 retries
126
+ expect(retryCount).to.equal(4);
127
+ expect(hostedFieldsSpy.callCount).to.equal(4);
128
+ }));
129
+ it('retries creating the hosted fields if they fail', () => __awaiter(void 0, void 0, void 0, function* () {
130
+ const braintreeManager = new MockBraintreeManager();
131
+ const client = new MockHostedFieldsClient();
132
+ let retryCount = 0;
133
+ Sinon.stub(client, 'create').callsFake(() => {
134
+ if (retryCount < 2) {
135
+ retryCount++;
136
+ throw new Error('Error');
137
+ }
138
+ return Promise.resolve(client);
139
+ });
140
+ const mockHostedFieldContainer = new MockHostedFieldContainer();
141
+ const mockHostedFieldConfig = new HostedFieldConfiguration({
142
+ hostedFieldStyle: mockHostedFieldStyle,
143
+ hostedFieldFieldOptions: mockHostedFieldFieldOptions,
144
+ hostedFieldContainer: mockHostedFieldContainer,
145
+ });
146
+ const handler = new CreditCardHandler({
147
+ braintreeManager: braintreeManager,
148
+ hostedFieldClient: client,
149
+ hostedFieldConfig: mockHostedFieldConfig,
150
+ retryInverval: 0.01,
151
+ maxRetryCount: 3,
152
+ loadTimeout: 0.01,
153
+ });
154
+ const instance = yield handler.instance.get();
155
+ expect(instance).to.not.be.null;
156
+ expect(retryCount).to.equal(2);
157
+ }));
158
+ it('emits an event when retrying or failing', () => __awaiter(void 0, void 0, void 0, function* () {
159
+ const braintreeManager = new MockBraintreeManager();
160
+ const client = new MockHostedFieldsClient();
161
+ Sinon.stub(client, 'create').callsFake(() => {
162
+ throw new Error('Error');
163
+ });
164
+ const mockHostedFieldContainer = new MockHostedFieldContainer();
165
+ const mockHostedFieldConfig = new HostedFieldConfiguration({
166
+ hostedFieldStyle: mockHostedFieldStyle,
167
+ hostedFieldFieldOptions: mockHostedFieldFieldOptions,
168
+ hostedFieldContainer: mockHostedFieldContainer,
169
+ });
170
+ const handler = new CreditCardHandler({
171
+ braintreeManager: braintreeManager,
172
+ hostedFieldClient: client,
173
+ hostedFieldConfig: mockHostedFieldConfig,
174
+ retryInverval: 0.01,
175
+ maxRetryCount: 3,
176
+ loadTimeout: 0.01,
177
+ });
178
+ let retryCountEvent = 0;
179
+ let retryFailedEvent = 0;
180
+ handler.on('hostedFieldsRetry', () => {
181
+ retryCountEvent++;
182
+ });
183
+ handler.on('hostedFieldsFailed', () => {
184
+ retryFailedEvent++;
185
+ });
186
+ try {
187
+ yield handler.instance.get();
188
+ }
189
+ catch (e) { }
190
+ expect(retryCountEvent).to.equal(3);
191
+ expect(retryFailedEvent).to.equal(1);
192
+ }));
92
193
  });
93
194
  //# sourceMappingURL=creditcard.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"creditcard.test.js","sourceRoot":"","sources":["../../../../test/tests/payment-providers/creditcard.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect } from '@open-wc/testing';\nimport { MockBraintreeManager } from '../../mocks/mock-braintree-manager';\nimport { MockHostedFieldsClient } from '../../mocks/payment-clients/mock-hostedfields-client';\nimport { mockHostedFieldTokenizePayload } from '../../mocks/payment-clients/mock-hostedfieldtokenizepayload';\nimport { CreditCardHandler } from '../../../src/braintree-manager/payment-providers/credit-card/credit-card';\nimport { MockHostedFieldContainer } from '../../mocks/mock-hosted-fields-container';\nimport { HostedFieldConfiguration } from '../../../src/braintree-manager/payment-providers/credit-card/hosted-field-configuration';\nimport {\n mockHostedFieldStyle,\n mockHostedFieldFieldOptions,\n mockHostedFieldConfig,\n} from '../../mocks/mock-hosted-fields-config';\n\ndescribe('CreditCardHandler', () => {\n it('can tokenize the hosted fields', async () => {\n const braintreeManager = new MockBraintreeManager();\n const client = new MockHostedFieldsClient({\n mockHostedFieldTokenizePayload: mockHostedFieldTokenizePayload,\n });\n\n const handler = new CreditCardHandler({\n braintreeManager: braintreeManager,\n hostedFieldClient: client,\n hostedFieldConfig: mockHostedFieldConfig,\n });\n\n const payload = await handler.tokenizeHostedFields();\n\n expect(payload?.nonce).to.equal(mockHostedFieldTokenizePayload.nonce);\n });\n\n it('can mark field errors', async () => {\n const braintreeManager = new MockBraintreeManager();\n const client = new MockHostedFieldsClient();\n const mockHostedFieldContainer = new MockHostedFieldContainer();\n const mockHostedFieldConfig: HostedFieldConfiguration = new HostedFieldConfiguration({\n hostedFieldStyle: mockHostedFieldStyle,\n hostedFieldFieldOptions: mockHostedFieldFieldOptions,\n hostedFieldContainer: mockHostedFieldContainer,\n });\n const handler = new CreditCardHandler({\n braintreeManager: braintreeManager,\n hostedFieldClient: client,\n hostedFieldConfig: mockHostedFieldConfig,\n });\n handler.markFieldErrors([]);\n expect(mockHostedFieldContainer.markErrorsCalled).to.be.true;\n });\n\n it('can remove field errors', async () => {\n const braintreeManager = new MockBraintreeManager();\n const client = new MockHostedFieldsClient();\n const mockHostedFieldContainer = new MockHostedFieldContainer();\n const mockHostedFieldConfig: HostedFieldConfiguration = new HostedFieldConfiguration({\n hostedFieldStyle: mockHostedFieldStyle,\n hostedFieldFieldOptions: mockHostedFieldFieldOptions,\n hostedFieldContainer: mockHostedFieldContainer,\n });\n const handler = new CreditCardHandler({\n braintreeManager: braintreeManager,\n hostedFieldClient: client,\n hostedFieldConfig: mockHostedFieldConfig,\n });\n handler.removeFieldErrors([]);\n expect(mockHostedFieldContainer.removeErrorsCalled).to.be.true;\n });\n\n it('can show the error message', async () => {\n const braintreeManager = new MockBraintreeManager();\n const client = new MockHostedFieldsClient();\n const mockHostedFieldContainer = new MockHostedFieldContainer();\n const mockHostedFieldConfig: HostedFieldConfiguration = new HostedFieldConfiguration({\n hostedFieldStyle: mockHostedFieldStyle,\n hostedFieldFieldOptions: mockHostedFieldFieldOptions,\n hostedFieldContainer: mockHostedFieldContainer,\n });\n const handler = new CreditCardHandler({\n braintreeManager: braintreeManager,\n hostedFieldClient: client,\n hostedFieldConfig: mockHostedFieldConfig,\n });\n handler.showErrorMessage();\n expect(mockHostedFieldContainer.showErrorMessageCalled).to.be.true;\n });\n\n it('can hide the error message', async () => {\n const braintreeManager = new MockBraintreeManager();\n const client = new MockHostedFieldsClient();\n const mockHostedFieldContainer = new MockHostedFieldContainer();\n const mockHostedFieldConfig: HostedFieldConfiguration = new HostedFieldConfiguration({\n hostedFieldStyle: mockHostedFieldStyle,\n hostedFieldFieldOptions: mockHostedFieldFieldOptions,\n hostedFieldContainer: mockHostedFieldContainer,\n });\n const handler = new CreditCardHandler({\n braintreeManager: braintreeManager,\n hostedFieldClient: client,\n hostedFieldConfig: mockHostedFieldConfig,\n });\n handler.hideErrorMessage();\n expect(mockHostedFieldContainer.hideErrorMessageCalled).to.be.true;\n });\n});\n"]}
1
+ {"version":3,"file":"creditcard.test.js","sourceRoot":"","sources":["../../../../test/tests/payment-providers/creditcard.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect } from '@open-wc/testing';\nimport { MockBraintreeManager } from '../../mocks/mock-braintree-manager';\nimport { MockHostedFieldsClient } from '../../mocks/payment-clients/mock-hostedfields-client';\nimport { mockHostedFieldTokenizePayload } from '../../mocks/payment-clients/mock-hostedfieldtokenizepayload';\nimport { CreditCardHandler } from '../../../src/braintree-manager/payment-providers/credit-card/credit-card';\nimport { MockHostedFieldContainer } from '../../mocks/mock-hosted-fields-container';\nimport { HostedFieldConfiguration } from '../../../src/braintree-manager/payment-providers/credit-card/hosted-field-configuration';\nimport Sinon from 'sinon';\nimport {\n mockHostedFieldStyle,\n mockHostedFieldFieldOptions,\n mockHostedFieldConfig,\n} from '../../mocks/mock-hosted-fields-config';\n\nconst sandbox = Sinon.createSandbox();\n\ndescribe('CreditCardHandler', () => {\n afterEach(() => {\n sandbox.restore();\n });\n\n it('can tokenize the hosted fields', async () => {\n const braintreeManager = new MockBraintreeManager();\n const client = new MockHostedFieldsClient({\n mockHostedFieldTokenizePayload: mockHostedFieldTokenizePayload,\n });\n\n const handler = new CreditCardHandler({\n braintreeManager: braintreeManager,\n hostedFieldClient: client,\n hostedFieldConfig: mockHostedFieldConfig,\n });\n\n const payload = await handler.tokenizeHostedFields();\n\n expect(payload?.nonce).to.equal(mockHostedFieldTokenizePayload.nonce);\n });\n\n it('can mark field errors', async () => {\n const braintreeManager = new MockBraintreeManager();\n const client = new MockHostedFieldsClient();\n const mockHostedFieldContainer = new MockHostedFieldContainer();\n const mockHostedFieldConfig: HostedFieldConfiguration = new HostedFieldConfiguration({\n hostedFieldStyle: mockHostedFieldStyle,\n hostedFieldFieldOptions: mockHostedFieldFieldOptions,\n hostedFieldContainer: mockHostedFieldContainer,\n });\n const handler = new CreditCardHandler({\n braintreeManager: braintreeManager,\n hostedFieldClient: client,\n hostedFieldConfig: mockHostedFieldConfig,\n });\n handler.markFieldErrors([]);\n expect(mockHostedFieldContainer.markErrorsCalled).to.be.true;\n });\n\n it('can remove field errors', async () => {\n const braintreeManager = new MockBraintreeManager();\n const client = new MockHostedFieldsClient();\n const mockHostedFieldContainer = new MockHostedFieldContainer();\n const mockHostedFieldConfig: HostedFieldConfiguration = new HostedFieldConfiguration({\n hostedFieldStyle: mockHostedFieldStyle,\n hostedFieldFieldOptions: mockHostedFieldFieldOptions,\n hostedFieldContainer: mockHostedFieldContainer,\n });\n const handler = new CreditCardHandler({\n braintreeManager: braintreeManager,\n hostedFieldClient: client,\n hostedFieldConfig: mockHostedFieldConfig,\n });\n handler.removeFieldErrors([]);\n expect(mockHostedFieldContainer.removeErrorsCalled).to.be.true;\n });\n\n it('can show the error message', async () => {\n const braintreeManager = new MockBraintreeManager();\n const client = new MockHostedFieldsClient();\n const mockHostedFieldContainer = new MockHostedFieldContainer();\n const mockHostedFieldConfig: HostedFieldConfiguration = new HostedFieldConfiguration({\n hostedFieldStyle: mockHostedFieldStyle,\n hostedFieldFieldOptions: mockHostedFieldFieldOptions,\n hostedFieldContainer: mockHostedFieldContainer,\n });\n const handler = new CreditCardHandler({\n braintreeManager: braintreeManager,\n hostedFieldClient: client,\n hostedFieldConfig: mockHostedFieldConfig,\n });\n handler.showErrorMessage();\n expect(mockHostedFieldContainer.showErrorMessageCalled).to.be.true;\n });\n\n it('can hide the error message', async () => {\n const braintreeManager = new MockBraintreeManager();\n const client = new MockHostedFieldsClient();\n const mockHostedFieldContainer = new MockHostedFieldContainer();\n const mockHostedFieldConfig: HostedFieldConfiguration = new HostedFieldConfiguration({\n hostedFieldStyle: mockHostedFieldStyle,\n hostedFieldFieldOptions: mockHostedFieldFieldOptions,\n hostedFieldContainer: mockHostedFieldContainer,\n });\n const handler = new CreditCardHandler({\n braintreeManager: braintreeManager,\n hostedFieldClient: client,\n hostedFieldConfig: mockHostedFieldConfig,\n });\n handler.hideErrorMessage();\n expect(mockHostedFieldContainer.hideErrorMessageCalled).to.be.true;\n });\n\n it('retries the expected number of times before failure', async () => {\n const braintreeManager = new MockBraintreeManager();\n const client = new MockHostedFieldsClient();\n\n let retryCount = 0;\n Sinon.stub(client, 'create').callsFake(() => {\n retryCount++;\n throw new Error('Error');\n });\n\n const mockHostedFieldContainer = new MockHostedFieldContainer();\n const hostedFieldsSpy = Sinon.spy(mockHostedFieldContainer, 'resetHostedFields');\n\n const mockHostedFieldConfig: HostedFieldConfiguration = new HostedFieldConfiguration({\n hostedFieldStyle: mockHostedFieldStyle,\n hostedFieldFieldOptions: mockHostedFieldFieldOptions,\n hostedFieldContainer: mockHostedFieldContainer,\n });\n const handler = new CreditCardHandler({\n braintreeManager: braintreeManager,\n hostedFieldClient: client,\n hostedFieldConfig: mockHostedFieldConfig,\n retryInverval: 0.01,\n maxRetryCount: 3,\n loadTimeout: 0.01,\n });\n\n try {\n await handler.instance.get();\n expect.fail('Should have thrown an error');\n } catch (e) {}\n\n // initial call + 3 retries\n expect(retryCount).to.equal(4);\n expect(hostedFieldsSpy.callCount).to.equal(4);\n });\n\n it('retries creating the hosted fields if they fail', async () => {\n const braintreeManager = new MockBraintreeManager();\n const client = new MockHostedFieldsClient();\n\n let retryCount = 0;\n Sinon.stub(client, 'create').callsFake(() => {\n if (retryCount < 2) {\n retryCount++;\n throw new Error('Error');\n }\n return Promise.resolve(client);\n });\n\n const mockHostedFieldContainer = new MockHostedFieldContainer();\n const mockHostedFieldConfig: HostedFieldConfiguration = new HostedFieldConfiguration({\n hostedFieldStyle: mockHostedFieldStyle,\n hostedFieldFieldOptions: mockHostedFieldFieldOptions,\n hostedFieldContainer: mockHostedFieldContainer,\n });\n const handler = new CreditCardHandler({\n braintreeManager: braintreeManager,\n hostedFieldClient: client,\n hostedFieldConfig: mockHostedFieldConfig,\n retryInverval: 0.01,\n maxRetryCount: 3,\n loadTimeout: 0.01,\n });\n\n const instance = await handler.instance.get();\n expect(instance).to.not.be.null;\n expect(retryCount).to.equal(2);\n });\n\n it('emits an event when retrying or failing', async () => {\n const braintreeManager = new MockBraintreeManager();\n const client = new MockHostedFieldsClient();\n\n Sinon.stub(client, 'create').callsFake(() => {\n throw new Error('Error');\n });\n\n const mockHostedFieldContainer = new MockHostedFieldContainer();\n const mockHostedFieldConfig: HostedFieldConfiguration = new HostedFieldConfiguration({\n hostedFieldStyle: mockHostedFieldStyle,\n hostedFieldFieldOptions: mockHostedFieldFieldOptions,\n hostedFieldContainer: mockHostedFieldContainer,\n });\n const handler = new CreditCardHandler({\n braintreeManager: braintreeManager,\n hostedFieldClient: client,\n hostedFieldConfig: mockHostedFieldConfig,\n retryInverval: 0.01,\n maxRetryCount: 3,\n loadTimeout: 0.01,\n });\n\n let retryCountEvent = 0;\n let retryFailedEvent = 0;\n handler.on('hostedFieldsRetry', () => {\n retryCountEvent++;\n });\n handler.on('hostedFieldsFailed', () => {\n retryFailedEvent++;\n });\n\n try {\n await handler.instance.get();\n } catch (e) {}\n\n expect(retryCountEvent).to.equal(3);\n expect(retryFailedEvent).to.equal(1);\n });\n});\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@internetarchive/donation-form",
3
- "version": "0.5.8",
3
+ "version": "0.5.9-alpha.1",
4
4
  "description": "The Internet Archive Donation Form",
5
5
  "license": "AGPL-3.0-only",
6
6
  "main": "dist/index.js",
@@ -72,6 +72,7 @@
72
72
  "lit-html": "^2.0.2",
73
73
  "madge": "^4.0.1",
74
74
  "polymer-cli": "^1.9.11",
75
+ "sinon": "^12.0.1",
75
76
  "typescript": "^3.9.5",
76
77
  "webpack-merge": "^4.1.5"
77
78
  },
@@ -8,8 +8,14 @@ import {
8
8
  BillingInfo,
9
9
  } from '@internetarchive/donation-form-data-models';
10
10
  import { PromisedSingleton } from '@internetarchive/promised-singleton';
11
+ import { Unsubscribe } from 'nanoevents';
11
12
  import { PaymentProvidersInterface } from './payment-providers-interface';
12
13
 
14
+ export interface BraintreeManagerEvents {
15
+ paymentProvidersHostedFieldsRetry: (retryNumber: number) => void;
16
+ paymentProvidersHostedFieldsFailed: (error: unknown) => void;
17
+ }
18
+
13
19
  /**
14
20
  * The BraintreeManager is the main entrypoint for much of the common braintree functionality.
15
21
  *
@@ -19,6 +25,11 @@ import { PaymentProvidersInterface } from './payment-providers-interface';
19
25
  * @interface BraintreeManagerInterface
20
26
  */
21
27
  export interface BraintreeManagerInterface {
28
+ on<E extends keyof BraintreeManagerEvents>(
29
+ event: E,
30
+ callback: BraintreeManagerEvents[E],
31
+ ): Unsubscribe;
32
+
22
33
  /**
23
34
  * The PaymentProviders class contains the IA-specific handlers for each of the
24
35
  * different payment providers.
@@ -16,10 +16,12 @@ import {
16
16
  BraintreeManagerInterface,
17
17
  BraintreeEndpointManagerInterface,
18
18
  HostingEnvironment,
19
+ BraintreeManagerEvents,
19
20
  } from './braintree-interfaces';
20
21
  import { HostedFieldConfiguration } from './payment-providers/credit-card/hosted-field-configuration';
21
22
  import { PromisedSingleton } from '@internetarchive/promised-singleton';
22
23
  import { PaymentProvidersInterface } from './payment-providers-interface';
24
+ import { createNanoEvents, Emitter, Unsubscribe } from 'nanoevents';
23
25
 
24
26
  /** @inheritdoc */
25
27
  export class BraintreeManager implements BraintreeManagerInterface {
@@ -58,6 +60,15 @@ export class BraintreeManager implements BraintreeManagerInterface {
58
60
  */
59
61
  private deviceData?: string;
60
62
 
63
+ private emitter = createNanoEvents<BraintreeManagerEvents>();
64
+
65
+ on<E extends keyof BraintreeManagerEvents>(
66
+ event: E,
67
+ callback: BraintreeManagerEvents[E],
68
+ ): Unsubscribe {
69
+ return this.emitter.on(event, callback);
70
+ }
71
+
61
72
  /** @inheritdoc */
62
73
  paymentProviders: PaymentProvidersInterface;
63
74
 
@@ -245,6 +256,14 @@ export class BraintreeManager implements BraintreeManagerInterface {
245
256
  hostingEnvironment: options.hostingEnvironment,
246
257
  hostedFieldConfig: options.hostedFieldConfig,
247
258
  });
259
+
260
+ this.paymentProviders.on('hostedFieldsRetry', (retryNumber: number) => {
261
+ this.emitter.emit('paymentProvidersHostedFieldsRetry', retryNumber);
262
+ });
263
+
264
+ this.paymentProviders.on('hostedFieldsFailed', (error: unknown) => {
265
+ this.emitter.emit('paymentProvidersHostedFieldsFailed', error);
266
+ });
248
267
  }
249
268
 
250
269
  /** @inheritdoc */
@@ -1,6 +1,12 @@
1
1
  import { PromisedSingleton } from '@internetarchive/promised-singleton';
2
+ import { Unsubscribe } from 'nanoevents';
2
3
  import { HostedFieldName } from './hosted-field-container';
3
4
 
5
+ export interface CreditCardHandlerEvents {
6
+ hostedFieldsRetry: (retryNumber: number) => void;
7
+ hostedFieldsFailed: (error: unknown) => void;
8
+ }
9
+
4
10
  export interface CreditCardHandlerInterface {
5
11
  instance: PromisedSingleton<braintree.HostedFields | undefined>;
6
12
  tokenizeHostedFields(): Promise<braintree.HostedFieldsTokenizePayload | undefined>;
@@ -8,4 +14,8 @@ export interface CreditCardHandlerInterface {
8
14
  removeFieldErrors(fields: HostedFieldName[]): void;
9
15
  showErrorMessage(message?: string): void;
10
16
  hideErrorMessage(): void;
17
+ on<E extends keyof CreditCardHandlerEvents>(
18
+ event: E,
19
+ callback: CreditCardHandlerEvents[E],
20
+ ): Unsubscribe;
11
21
  }
@@ -2,35 +2,102 @@ import { PromisedSingleton } from '@internetarchive/promised-singleton';
2
2
  import { BraintreeManagerInterface } from '../../braintree-interfaces';
3
3
  import { HostedFieldConfiguration } from './hosted-field-configuration';
4
4
  import { HostedFieldName } from './hosted-field-container';
5
- import { CreditCardHandlerInterface } from './credit-card-interface';
5
+ import { CreditCardHandlerEvents, CreditCardHandlerInterface } from './credit-card-interface';
6
+ import { promisedSleep } from '../../../util/promisedSleep';
7
+ import { createNanoEvents, Unsubscribe } from 'nanoevents';
6
8
 
7
9
  export class CreditCardHandler implements CreditCardHandlerInterface {
8
- instance: PromisedSingleton<braintree.HostedFields | undefined>;
10
+ on<E extends keyof CreditCardHandlerEvents>(
11
+ event: E,
12
+ callback: CreditCardHandlerEvents[E],
13
+ ): Unsubscribe {
14
+ return this.emitter.on(event, callback);
15
+ }
16
+
17
+ instance = new PromisedSingleton<braintree.HostedFields | undefined>({
18
+ generator: async (): Promise<braintree.HostedFields | undefined> => {
19
+ const braintreeClient = await this.braintreeManager.instance.get();
20
+ const hostedFields = await this.createHostedFields(braintreeClient);
21
+ return hostedFields;
22
+ },
23
+ });
24
+
25
+ private emitter = createNanoEvents<CreditCardHandlerEvents>();
26
+
27
+ private maxRetryCount: number;
28
+
29
+ private retryInverval: number;
30
+
31
+ private loadTimeout: number;
9
32
 
10
33
  constructor(options: {
11
34
  braintreeManager: BraintreeManagerInterface;
12
35
  hostedFieldClient: braintree.HostedFields;
13
36
  hostedFieldConfig: HostedFieldConfiguration;
37
+ maxRetryCount?: number;
38
+ retryInverval?: number;
39
+ loadTimeout?: number;
14
40
  }) {
15
41
  this.braintreeManager = options.braintreeManager;
16
42
  this.hostedFieldClient = options.hostedFieldClient;
17
43
  this.hostedFieldConfig = options.hostedFieldConfig;
44
+ this.maxRetryCount = options.maxRetryCount ?? 2;
45
+ this.retryInverval = (options.retryInverval ?? 1) * 1000;
46
+ this.loadTimeout = (options.loadTimeout ?? 6) * 1000;
47
+ }
48
+
49
+ private braintreeManager: BraintreeManagerInterface;
50
+ private hostedFieldClient: braintree.HostedFields;
51
+ private hostedFieldConfig: HostedFieldConfiguration;
52
+
53
+ private async createHostedFields(
54
+ braintreeClient: braintree.Client,
55
+ retryCount = 0,
56
+ ): Promise<braintree.HostedFields | undefined> {
57
+ // we mainly want to do this for retry events, but it doesn't
58
+ // hurt to do it on the first try
59
+ this.hostedFieldConfig.hostedFieldContainer.resetHostedFields();
60
+ try {
61
+ // The hosted fields have a 60 second timeout internally, but braintree
62
+ // support recommended setting a shorter timeout because 99% of users
63
+ // load the hosted fields in under 4 seconds.
64
+ // What we're doing here is creating a "timeout" promise
65
+ // and a "create hosted fields" promise and doing a `Promise.race()` to
66
+ // resolve when the first one finishes. If the timeout finishes first,
67
+ // we throw an error to trigger the retry logic. If the hosted fields
68
+ // finishes first, we cancel the timeout promise since we're done.
69
+ let timeout: number;
70
+ const timeoutPromise = new Promise<void>((resolve, reject) => {
71
+ timeout = window.setTimeout(() => {
72
+ reject(new Error('Timeout loading Hosted Fields'));
73
+ }, this.loadTimeout);
74
+ });
18
75
 
19
- this.instance = new PromisedSingleton<braintree.HostedFields | undefined>({
20
- generator: async (): Promise<braintree.HostedFields | undefined> => {
21
- const braintreeClient = await this.braintreeManager.instance.get();
22
- return this.hostedFieldClient.create({
76
+ const hostedFieldsPromise = new Promise<braintree.HostedFields | undefined>(async resolve => {
77
+ const fields = await this.hostedFieldClient.create({
23
78
  client: braintreeClient,
24
79
  styles: this.hostedFieldConfig.hostedFieldStyle,
25
80
  fields: this.hostedFieldConfig.hostedFieldFieldOptions,
26
81
  });
27
- },
28
- });
29
- }
82
+ // clear the timeout when this finishes so we don't also get the timeout rejection
83
+ window.clearTimeout(timeout);
84
+ resolve(fields);
85
+ });
30
86
 
31
- private braintreeManager: BraintreeManagerInterface;
32
- private hostedFieldClient: braintree.HostedFields;
33
- private hostedFieldConfig: HostedFieldConfiguration;
87
+ const result = await Promise.race([timeoutPromise, hostedFieldsPromise]);
88
+
89
+ return result as braintree.HostedFields;
90
+ } catch (error) {
91
+ if (retryCount >= this.maxRetryCount) {
92
+ this.emitter.emit('hostedFieldsFailed', error);
93
+ throw error;
94
+ }
95
+ await promisedSleep(this.retryInverval); // wait before retrying
96
+ const newRetryCount = retryCount + 1;
97
+ this.emitter.emit('hostedFieldsRetry', newRetryCount);
98
+ return this.createHostedFields(braintreeClient, newRetryCount);
99
+ }
100
+ }
34
101
 
35
102
  async tokenizeHostedFields(): Promise<braintree.HostedFieldsTokenizePayload | undefined> {
36
103
  const hostedFields = await this.instance.get();
@@ -7,23 +7,33 @@ export enum HostedFieldName {
7
7
  }
8
8
 
9
9
  export interface HostedFieldContainerInterface {
10
- fieldFor(field: HostedFieldName): HTMLInputElement;
10
+ fieldFor(field: HostedFieldName): HTMLDivElement;
11
11
  markFieldErrors(fields: HostedFieldName[]): void;
12
12
  removeFieldErrors(fields: HostedFieldName[]): void;
13
13
  showErrorMessage(message?: string): void;
14
14
  hideErrorMessage(): void;
15
+ /**
16
+ * Determine if the hosted fields have loaded.
17
+ *
18
+ * This is used to detect timeouts on the credit card hosted fields.
19
+ */
20
+ allHostedFieldsAreLoaded(): boolean;
21
+ /**
22
+ * Reset the hosted fields to retry in case of timeout
23
+ */
24
+ resetHostedFields(): void;
15
25
  }
16
26
 
17
27
  export class HostedFieldContainer implements HostedFieldContainerInterface {
18
- private number: HTMLInputElement;
28
+ private number: HTMLDivElement;
19
29
 
20
- private cvv: HTMLInputElement;
30
+ private cvv: HTMLDivElement;
21
31
 
22
- private expirationDate: HTMLInputElement;
32
+ private expirationDate: HTMLDivElement;
23
33
 
24
34
  private errorContainer: HTMLDivElement;
25
35
 
26
- fieldFor(field: HostedFieldName): HTMLInputElement {
36
+ fieldFor(field: HostedFieldName): HTMLDivElement {
27
37
  switch (field) {
28
38
  case HostedFieldName.Number:
29
39
  return this.number;
@@ -58,10 +68,27 @@ export class HostedFieldContainer implements HostedFieldContainerInterface {
58
68
  this.errorContainer.style.display = 'none';
59
69
  }
60
70
 
71
+ /** @inheritdoc */
72
+ allHostedFieldsAreLoaded(): boolean {
73
+ const elements = [this.number, this.cvv, this.expirationDate];
74
+ return elements.every(element => {
75
+ return element.firstChild !== null;
76
+ });
77
+ }
78
+
79
+ resetHostedFields(): void {
80
+ const elements = [this.number, this.cvv, this.expirationDate];
81
+ elements.forEach(element => {
82
+ while (element.firstChild) {
83
+ element.firstChild.remove();
84
+ }
85
+ });
86
+ }
87
+
61
88
  constructor(options: {
62
- number: HTMLInputElement;
63
- cvv: HTMLInputElement;
64
- expirationDate: HTMLInputElement;
89
+ number: HTMLDivElement;
90
+ cvv: HTMLDivElement;
91
+ expirationDate: HTMLDivElement;
65
92
  errorContainer: HTMLDivElement;
66
93
  }) {
67
94
  this.number = options.number;
@@ -1,11 +1,22 @@
1
1
  import { PromisedSingleton } from '@internetarchive/promised-singleton';
2
2
  import { ApplePayHandlerInterface } from './payment-providers/apple-pay/apple-pay-interface';
3
- import { CreditCardHandlerInterface } from './payment-providers/credit-card/credit-card-interface';
3
+ import {
4
+ CreditCardHandlerEvents,
5
+ CreditCardHandlerInterface,
6
+ } from './payment-providers/credit-card/credit-card-interface';
4
7
  import { VenmoHandlerInterface } from './payment-providers/venmo-interface';
5
8
  import { PayPalHandlerInterface } from './payment-providers/paypal/paypal-interface';
6
9
  import { GooglePayHandlerInterface } from './payment-providers/google-pay-interface';
10
+ import { Unsubscribe } from 'nanoevents';
11
+
12
+ // this will bubble up child events
13
+ export type PaymentProvidersEvents = CreditCardHandlerEvents;
7
14
 
8
15
  export interface PaymentProvidersInterface {
16
+ on<E extends keyof PaymentProvidersEvents>(
17
+ event: E,
18
+ callback: PaymentProvidersEvents[E],
19
+ ): Unsubscribe;
9
20
  creditCardHandler: PromisedSingleton<CreditCardHandlerInterface>;
10
21
  applePayHandler: PromisedSingleton<ApplePayHandlerInterface>;
11
22
  venmoHandler: PromisedSingleton<VenmoHandlerInterface | undefined>;
@@ -9,12 +9,13 @@ import { PaymentClientsInterface } from './payment-clients';
9
9
  import { GooglePayHandler } from './payment-providers/google-pay';
10
10
  import { BraintreeManagerInterface, HostingEnvironment } from './braintree-interfaces';
11
11
  import { HostedFieldConfiguration } from './payment-providers/credit-card/hosted-field-configuration';
12
- import { PaymentProvidersInterface } from './payment-providers-interface';
12
+ import { PaymentProvidersEvents, PaymentProvidersInterface } from './payment-providers-interface';
13
13
  import { ApplePayHandlerInterface } from './payment-providers/apple-pay/apple-pay-interface';
14
14
  import { CreditCardHandlerInterface } from './payment-providers/credit-card/credit-card-interface';
15
15
  import { VenmoHandlerInterface } from './payment-providers/venmo-interface';
16
16
  import { PayPalHandlerInterface } from './payment-providers/paypal/paypal-interface';
17
17
  import { GooglePayHandlerInterface } from './payment-providers/google-pay-interface';
18
+ import { createNanoEvents, Emitter, Unsubscribe } from 'nanoevents';
18
19
 
19
20
  /**
20
21
  * The PaymentProviders class contains the IA-specific handlers for each of the
@@ -28,14 +29,31 @@ import { GooglePayHandlerInterface } from './payment-providers/google-pay-interf
28
29
  * @implements {PaymentProvidersInterface}
29
30
  */
30
31
  export class PaymentProviders implements PaymentProvidersInterface {
32
+ on<E extends keyof PaymentProvidersEvents>(
33
+ event: E,
34
+ callback: PaymentProvidersEvents[E],
35
+ ): Unsubscribe {
36
+ return this.emitter.on(event, callback);
37
+ }
38
+
31
39
  creditCardHandler = new PromisedSingleton<CreditCardHandlerInterface>({
32
40
  generator: async (): Promise<CreditCardHandlerInterface> => {
33
41
  const client = await this.paymentClients.hostedFields.get();
34
- return new CreditCardHandler({
42
+ const handler = new CreditCardHandler({
35
43
  braintreeManager: this.braintreeManager,
36
44
  hostedFieldClient: client,
37
45
  hostedFieldConfig: this.hostedFieldConfig,
38
46
  });
47
+
48
+ handler.on('hostedFieldsRetry', (retryNumber: number) => {
49
+ this.emitter.emit('hostedFieldsRetry', retryNumber);
50
+ });
51
+
52
+ handler.on('hostedFieldsFailed', (error: unknown) => {
53
+ this.emitter.emit('hostedFieldsFailed', error);
54
+ });
55
+
56
+ return handler;
39
57
  },
40
58
  });
41
59
 
@@ -109,6 +127,8 @@ export class PaymentProviders implements PaymentProvidersInterface {
109
127
 
110
128
  private paymentClients: PaymentClientsInterface;
111
129
 
130
+ private emitter = createNanoEvents<PaymentProvidersEvents>();
131
+
112
132
  constructor(options: {
113
133
  braintreeManager: BraintreeManagerInterface;
114
134
  paymentClients: PaymentClientsInterface;
@@ -109,11 +109,11 @@ export class DonationFormController extends LitElement {
109
109
 
110
110
  @query('donation-form') private donationForm!: DonationForm;
111
111
 
112
- @query('#braintree-creditcard') private braintreeNumberInput!: HTMLInputElement;
112
+ @query('#braintree-creditcard') private braintreeNumberInput!: HTMLDivElement;
113
113
 
114
- @query('#braintree-cvv') private braintreeCVVInput!: HTMLInputElement;
114
+ @query('#braintree-cvv') private braintreeCVVInput!: HTMLDivElement;
115
115
 
116
- @query('#braintree-expiration') private braintreeExpirationDateInput!: HTMLInputElement;
116
+ @query('#braintree-expiration') private braintreeExpirationDateInput!: HTMLDivElement;
117
117
 
118
118
  @query('#braintree-error-message') private braintreeErrorMessage!: HTMLDivElement;
119
119
 
@@ -195,6 +195,20 @@ export class DonationFormController extends LitElement {
195
195
  loggedInUser: this.loggedInUser,
196
196
  origin: this.origin,
197
197
  });
198
+
199
+ this.braintreeManager.on('paymentProvidersHostedFieldsRetry', (retryNumber: number) => {
200
+ const event = new CustomEvent('paymentProvidersHostedFieldsRetry', {
201
+ detail: { retryNumber },
202
+ });
203
+ this.dispatchEvent(event);
204
+ });
205
+
206
+ this.braintreeManager.on('paymentProvidersHostedFieldsFailed', (error: unknown) => {
207
+ const event = new CustomEvent('paymentProvidersHostedFieldsFailed', {
208
+ detail: { error },
209
+ });
210
+ this.dispatchEvent(event);
211
+ });
198
212
  }
199
213
  }
200
214
 
@@ -0,0 +1,3 @@
1
+ export function promisedSleep(ms: number): Promise<void> {
2
+ return new Promise(resolve => setTimeout(resolve, ms));
3
+ }