@internetarchive/donation-form 1.0.3-alpha-webdev7960.1 → 1.0.3-webdev-8122.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/LICENSE +661 -661
- package/README.md +115 -115
- package/dist/demo/braintree-endpoint-manager.js.map +1 -1
- package/dist/demo/demo-analytics-handler.js.map +1 -1
- package/dist/demo/submit-form-with.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/src/braintree-manager/braintree-interfaces.js.map +1 -1
- package/dist/src/braintree-manager/braintree-manager.js.map +1 -1
- package/dist/src/braintree-manager/payment-clients.js.map +1 -1
- package/dist/src/braintree-manager/payment-providers/apple-pay/apple-pay-interface.js.map +1 -1
- package/dist/src/braintree-manager/payment-providers/apple-pay/apple-pay-session-datasource-delegate.js.map +1 -1
- package/dist/src/braintree-manager/payment-providers/apple-pay/apple-pay-session-datasource-interface.js.map +1 -1
- package/dist/src/braintree-manager/payment-providers/apple-pay/apple-pay-session-datasource.js.map +1 -1
- package/dist/src/braintree-manager/payment-providers/apple-pay/apple-pay-session-manager.js.map +1 -1
- package/dist/src/braintree-manager/payment-providers/apple-pay/apple-pay.js.map +1 -1
- package/dist/src/braintree-manager/payment-providers/credit-card/credit-card-interface.js.map +1 -1
- package/dist/src/braintree-manager/payment-providers/credit-card/credit-card.js.map +1 -1
- package/dist/src/braintree-manager/payment-providers/credit-card/hosted-field-configuration.js.map +1 -1
- package/dist/src/braintree-manager/payment-providers/credit-card/hosted-field-container.js.map +1 -1
- package/dist/src/braintree-manager/payment-providers/google-pay-interface.js.map +1 -1
- package/dist/src/braintree-manager/payment-providers/google-pay.js.map +1 -1
- package/dist/src/braintree-manager/payment-providers/paypal/paypal-button-datasource.js.map +1 -1
- package/dist/src/braintree-manager/payment-providers/paypal/paypal-interface.js.map +1 -1
- package/dist/src/braintree-manager/payment-providers/paypal/paypal.js.map +1 -1
- package/dist/src/braintree-manager/payment-providers/venmo-interface.js.map +1 -1
- package/dist/src/braintree-manager/payment-providers/venmo.js.map +1 -1
- package/dist/src/braintree-manager/payment-providers-interface.js.map +1 -1
- package/dist/src/braintree-manager/payment-providers.js.map +1 -1
- package/dist/src/donation-form-controller.js +123 -123
- package/dist/src/donation-form-controller.js.map +1 -1
- package/dist/src/donation-form-error.js.map +1 -1
- package/dist/src/donation-form.js +107 -107
- package/dist/src/donation-form.js.map +1 -1
- package/dist/src/form-elements/badged-input.js +47 -47
- package/dist/src/form-elements/badged-input.js.map +1 -1
- package/dist/src/form-elements/contact-form/autocomplete-field-options.js.map +1 -1
- package/dist/src/form-elements/contact-form/contact-form.js +159 -157
- package/dist/src/form-elements/contact-form/contact-form.js.map +1 -1
- package/dist/src/form-elements/contact-form/countries.js.map +1 -1
- package/dist/src/form-elements/header/donation-form-header.js +14 -14
- package/dist/src/form-elements/header/donation-form-header.js.map +1 -1
- package/dist/src/form-elements/header/donation-summary.js +15 -15
- package/dist/src/form-elements/header/donation-summary.js.map +1 -1
- package/dist/src/form-elements/payment-selector.js +164 -164
- package/dist/src/form-elements/payment-selector.js.map +1 -1
- package/dist/src/form-elements/total-amount.js +16 -16
- package/dist/src/form-elements/total-amount.js.map +1 -1
- package/dist/src/modals/confirm-donation-modal-content.js +51 -51
- package/dist/src/modals/confirm-donation-modal-content.js.map +1 -1
- package/dist/src/modals/error-modal-content.js +22 -22
- package/dist/src/modals/error-modal-content.js.map +1 -1
- package/dist/src/modals/upsell-modal-content.js +182 -182
- package/dist/src/modals/upsell-modal-content.js.map +1 -1
- package/dist/src/payment-flow-handlers/donation-flow-modal-manager.js +20 -20
- package/dist/src/payment-flow-handlers/donation-flow-modal-manager.js.map +1 -1
- package/dist/src/payment-flow-handlers/handlers/applepay-flow-handler.js.map +1 -1
- package/dist/src/payment-flow-handlers/handlers/creditcard-flow-handler.js.map +1 -1
- package/dist/src/payment-flow-handlers/handlers/googlepay-flow-handler.js +2 -0
- package/dist/src/payment-flow-handlers/handlers/googlepay-flow-handler.js.map +1 -1
- package/dist/src/payment-flow-handlers/handlers/paypal-flow-handler.js +2 -0
- package/dist/src/payment-flow-handlers/handlers/paypal-flow-handler.js.map +1 -1
- package/dist/src/payment-flow-handlers/handlers/venmo-flow-handler.js.map +1 -1
- package/dist/src/payment-flow-handlers/handlers/venmo-restoration-state-handler.js.map +1 -1
- package/dist/src/payment-flow-handlers/payment-flow-handlers.js.map +1 -1
- package/dist/src/recaptcha-manager/recaptcha-manager.js.map +1 -1
- package/dist/src/util/promisedSleep.js.map +1 -1
- package/dist/test/helpers/fillInContactForm.js.map +1 -1
- package/dist/test/mocks/flow-handlers/individual-handlers/mock-applepay-flow-handler.js.map +1 -1
- package/dist/test/mocks/flow-handlers/individual-handlers/mock-creditcard-flow-handler.js.map +1 -1
- package/dist/test/mocks/flow-handlers/individual-handlers/mock-googlepay-flow-handler.js.map +1 -1
- package/dist/test/mocks/flow-handlers/individual-handlers/mock-paypal-flow-handler.js.map +1 -1
- package/dist/test/mocks/flow-handlers/individual-handlers/mock-venmo-flow-handler.js.map +1 -1
- package/dist/test/mocks/flow-handlers/mock-payment-flow-handlers.js.map +1 -1
- package/dist/test/mocks/mock-braintree-manager.js.map +1 -1
- package/dist/test/mocks/mock-donation-info.js.map +1 -1
- package/dist/test/mocks/mock-endpoint-manager.js.map +1 -1
- package/dist/test/mocks/mock-hosted-fields-config.js.map +1 -1
- package/dist/test/mocks/mock-hosted-fields-container.js.map +1 -1
- package/dist/test/mocks/mock-lazy-loader.js.map +1 -1
- package/dist/test/mocks/mock-modal-manager.js.map +1 -1
- package/dist/test/mocks/mock-payment-clients.js.map +1 -1
- package/dist/test/mocks/mock-paypal-button-renderer.js.map +1 -1
- package/dist/test/mocks/mock-recaptcha-manager.js.map +1 -1
- package/dist/test/mocks/models/mock-billing-info.js +2 -0
- package/dist/test/mocks/models/mock-billing-info.js.map +1 -1
- package/dist/test/mocks/models/mock-custom-fields.js.map +1 -1
- package/dist/test/mocks/models/mock-customer-info.js.map +1 -1
- package/dist/test/mocks/models/mock-donation-request.js.map +1 -1
- package/dist/test/mocks/models/mock-success-response.js.map +1 -1
- package/dist/test/mocks/payment-clients/mock-applepay-client.js.map +1 -1
- package/dist/test/mocks/payment-clients/mock-applepay-payment.js.map +1 -1
- package/dist/test/mocks/payment-clients/mock-applepay-paymentauthorizedevent.js.map +1 -1
- package/dist/test/mocks/payment-clients/mock-applepay-session.js.map +1 -1
- package/dist/test/mocks/payment-clients/mock-applepay-sessionmanager.js.map +1 -1
- package/dist/test/mocks/payment-clients/mock-applepay-validatemerchantevent.js.map +1 -1
- package/dist/test/mocks/payment-clients/mock-braintree-client.js.map +1 -1
- package/dist/test/mocks/payment-clients/mock-data-collector.js.map +1 -1
- package/dist/test/mocks/payment-clients/mock-googlepay-client.js.map +1 -1
- package/dist/test/mocks/payment-clients/mock-googlepay-library.js.map +1 -1
- package/dist/test/mocks/payment-clients/mock-grecaptcha.js.map +1 -1
- package/dist/test/mocks/payment-clients/mock-hostedfields-client.js.map +1 -1
- package/dist/test/mocks/payment-clients/mock-hostedfieldstateobject-generator.js.map +1 -1
- package/dist/test/mocks/payment-clients/mock-hostedfieldtokenizepayload.js.map +1 -1
- package/dist/test/mocks/payment-clients/mock-paypal-client.js.map +1 -1
- package/dist/test/mocks/payment-clients/mock-paypal-library.js.map +1 -1
- package/dist/test/mocks/payment-clients/mock-venmo-client.js.map +1 -1
- package/dist/test/mocks/payment-providers/individual-providers/mock-applepay-datasource-delegate.js.map +1 -1
- package/dist/test/mocks/payment-providers/individual-providers/mock-applepay-handler.js.map +1 -1
- package/dist/test/mocks/payment-providers/individual-providers/mock-creditcard-handler.js.map +1 -1
- package/dist/test/mocks/payment-providers/individual-providers/mock-googlepay-handler.js.map +1 -1
- package/dist/test/mocks/payment-providers/individual-providers/mock-paypal-button-datasource-delegate.js.map +1 -1
- package/dist/test/mocks/payment-providers/individual-providers/mock-paypal-button-datasource.js.map +1 -1
- package/dist/test/mocks/payment-providers/individual-providers/mock-paypal-handler.js.map +1 -1
- package/dist/test/mocks/payment-providers/individual-providers/mock-venmo-handler.js.map +1 -1
- package/dist/test/mocks/payment-providers/mock-payment-providers.js.map +1 -1
- package/dist/test/tests/braintree-manager.test.js.map +1 -1
- package/dist/test/tests/donation-form-controller.test.js +39 -39
- package/dist/test/tests/donation-form-controller.test.js.map +1 -1
- package/dist/test/tests/donation-form.test.js +4 -4
- package/dist/test/tests/donation-form.test.js.map +1 -1
- package/dist/test/tests/flow-handlers/donation-flow-modal-manager.test.js +16 -14
- package/dist/test/tests/flow-handlers/donation-flow-modal-manager.test.js.map +1 -1
- package/dist/test/tests/form-elements/donation-summary.test.js.map +1 -1
- package/dist/test/tests/form-elements/payment-selector.test.js.map +1 -1
- package/dist/test/tests/modals/error-modal-content.test.js +2 -2
- package/dist/test/tests/modals/error-modal-content.test.js.map +1 -1
- package/dist/test/tests/modals/upsell-modal-content.test.js +31 -31
- package/dist/test/tests/modals/upsell-modal-content.test.js.map +1 -1
- package/dist/test/tests/models/donation-payment-info.test.js.map +1 -1
- package/dist/test/tests/payment-clients.test.js.map +1 -1
- package/dist/test/tests/payment-providers/applepay-sessiondatasource.test.js.map +1 -1
- package/dist/test/tests/payment-providers/applepay-sessionmanager.test.js.map +1 -1
- package/dist/test/tests/payment-providers/applepay.test.js.map +1 -1
- package/dist/test/tests/payment-providers/creditcard.test.js.map +1 -1
- package/dist/test/tests/payment-providers/googlepay.test.js.map +1 -1
- package/dist/test/tests/payment-providers/payment-providers.test.js.map +1 -1
- package/dist/test/tests/payment-providers/paypal-button-datasource.test.js.map +1 -1
- package/dist/test/tests/payment-providers/paypal.test.js.map +1 -1
- package/dist/test/tests/payment-providers/venmo.test.js.map +1 -1
- package/dist/test/tests/recaptcha-manager.test.js.map +1 -1
- package/package.json +107 -107
- package/src/@types/analytics-handler/index.d.ts +8 -8
- package/src/@types/braintree-web/LICENSE +21 -21
- package/src/@types/braintree-web/index.d.ts +93 -93
- package/src/@types/braintree-web/modules/american-express.d.ts +50 -50
- package/src/@types/braintree-web/modules/apple-pay.d.ts +213 -213
- package/src/@types/braintree-web/modules/client.d.ts +103 -103
- package/src/@types/braintree-web/modules/core.d.ts +34 -34
- package/src/@types/braintree-web/modules/data-collector.d.ts +13 -13
- package/src/@types/braintree-web/modules/google-payment.d.ts +269 -269
- package/src/@types/braintree-web/modules/hosted-fields.d.ts +366 -366
- package/src/@types/braintree-web/modules/paypal-checkout.d.ts +262 -262
- package/src/@types/braintree-web/modules/paypal.d.ts +177 -177
- package/src/@types/braintree-web/modules/three-d-secure.d.ts +141 -141
- package/src/@types/braintree-web/modules/unionpay.d.ts +224 -224
- package/src/@types/braintree-web/modules/us-bank-account.d.ts +81 -81
- package/src/@types/braintree-web/modules/venmo.d.ts +110 -110
- package/src/@types/braintree-web/package.json +64 -64
- package/src/@types/paypal-checkout-components/LICENSE +21 -21
- package/src/@types/paypal-checkout-components/index.d.ts +67 -67
- package/src/@types/paypal-checkout-components/modules/button.d.ts +50 -50
- package/src/@types/paypal-checkout-components/modules/callback-data.d.ts +244 -244
- package/src/@types/paypal-checkout-components/modules/configuration.d.ts +114 -114
- package/src/@types/paypal-checkout-components/package.json +58 -58
- package/src/braintree-manager/braintree-interfaces.ts +172 -172
- package/src/braintree-manager/braintree-manager.ts +281 -281
- package/src/braintree-manager/payment-clients.ts +146 -146
- package/src/braintree-manager/payment-providers/apple-pay/apple-pay-interface.ts +13 -13
- package/src/braintree-manager/payment-providers/apple-pay/apple-pay-session-datasource-delegate.ts +8 -8
- package/src/braintree-manager/payment-providers/apple-pay/apple-pay-session-datasource-interface.ts +10 -10
- package/src/braintree-manager/payment-providers/apple-pay/apple-pay-session-datasource.ts +119 -119
- package/src/braintree-manager/payment-providers/apple-pay/apple-pay-session-manager.ts +21 -21
- package/src/braintree-manager/payment-providers/apple-pay/apple-pay.ts +97 -97
- package/src/braintree-manager/payment-providers/credit-card/credit-card-interface.ts +21 -21
- package/src/braintree-manager/payment-providers/credit-card/credit-card.ts +130 -130
- package/src/braintree-manager/payment-providers/credit-card/hosted-field-configuration.ts +19 -19
- package/src/braintree-manager/payment-providers/credit-card/hosted-field-container.ts +85 -85
- package/src/braintree-manager/payment-providers/google-pay-interface.ts +8 -8
- package/src/braintree-manager/payment-providers/google-pay.ts +59 -59
- package/src/braintree-manager/payment-providers/paypal/paypal-button-datasource.ts +218 -218
- package/src/braintree-manager/payment-providers/paypal/paypal-interface.ts +13 -13
- package/src/braintree-manager/payment-providers/paypal/paypal.ts +78 -78
- package/src/braintree-manager/payment-providers/venmo-interface.ts +8 -8
- package/src/braintree-manager/payment-providers/venmo.ts +67 -67
- package/src/braintree-manager/payment-providers-interface.ts +25 -25
- package/src/braintree-manager/payment-providers.ts +147 -147
- package/src/donation-form-controller.ts +623 -623
- package/src/donation-form-error.ts +6 -6
- package/src/donation-form.ts +576 -576
- package/src/form-elements/badged-input.ts +109 -109
- package/src/form-elements/contact-form/autocomplete-field-options.ts +63 -63
- package/src/form-elements/contact-form/contact-form.ts +436 -434
- package/src/form-elements/contact-form/countries.ts +252 -252
- package/src/form-elements/header/donation-form-header.ts +98 -98
- package/src/form-elements/header/donation-summary.ts +61 -61
- package/src/form-elements/payment-selector.ts +365 -365
- package/src/form-elements/total-amount.ts +46 -46
- package/src/modals/confirm-donation-modal-content.ts +168 -168
- package/src/modals/error-modal-content.ts +48 -48
- package/src/modals/upsell-modal-content.ts +284 -284
- package/src/payment-flow-handlers/donation-flow-modal-manager.ts +439 -439
- package/src/payment-flow-handlers/handlers/applepay-flow-handler.ts +109 -109
- package/src/payment-flow-handlers/handlers/creditcard-flow-handler.ts +232 -232
- package/src/payment-flow-handlers/handlers/googlepay-flow-handler.ts +113 -111
- package/src/payment-flow-handlers/handlers/paypal-flow-handler.ts +333 -331
- package/src/payment-flow-handlers/handlers/venmo-flow-handler.ts +119 -119
- package/src/payment-flow-handlers/handlers/venmo-restoration-state-handler.ts +127 -127
- package/src/payment-flow-handlers/payment-flow-handlers.ts +218 -218
- package/src/recaptcha-manager/recaptcha-manager.ts +123 -123
- package/src/util/promisedSleep.ts +3 -3
|
@@ -1,232 +1,232 @@
|
|
|
1
|
-
import { createNanoEvents, Emitter, Unsubscribe } from 'nanoevents';
|
|
2
|
-
|
|
3
|
-
import { BraintreeManagerInterface } from '../../braintree-manager/braintree-interfaces';
|
|
4
|
-
import { RecaptchaManagerInterface } from '../../recaptcha-manager/recaptcha-manager';
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
DonorContactInfo,
|
|
8
|
-
DonationPaymentInfo,
|
|
9
|
-
PaymentProvider,
|
|
10
|
-
} from '@internetarchive/donation-form-data-models';
|
|
11
|
-
import { DonationFlowModalManagerInterface } from '../donation-flow-modal-manager';
|
|
12
|
-
import { HostedFieldName } from '../../braintree-manager/payment-providers/credit-card/hosted-field-container';
|
|
13
|
-
import { BadgedInput } from '../../form-elements/badged-input';
|
|
14
|
-
import { BraintreeError } from '../../@types/braintree-web';
|
|
15
|
-
|
|
16
|
-
export interface CreditCardFlowHandlerInterface {
|
|
17
|
-
/**
|
|
18
|
-
* Set up the hosted fields with event bindings
|
|
19
|
-
*
|
|
20
|
-
* @returns {Promise<void>}
|
|
21
|
-
* @memberof CreditCardFlowHandlerInterface
|
|
22
|
-
*/
|
|
23
|
-
startup(): Promise<void>;
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Tokenize the hosted fields.
|
|
27
|
-
*
|
|
28
|
-
* This also performs validation so we can call this before initiating the payment to
|
|
29
|
-
* display any error messaging.
|
|
30
|
-
*
|
|
31
|
-
* @returns {(Promise<braintree.HostedFieldsTokenizePayload | undefined>)}
|
|
32
|
-
* @memberof CreditCardFlowHandlerInterface
|
|
33
|
-
*/
|
|
34
|
-
tokenizeFields(): Promise<braintree.HostedFieldsTokenizePayload | undefined>;
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Initiate the payment flow.
|
|
38
|
-
*
|
|
39
|
-
* You must get the response from `tokenizeFields()` first before this to validate the hosted fields.
|
|
40
|
-
*
|
|
41
|
-
* @param {braintree.HostedFieldsTokenizePayload} hostedFieldsResponse
|
|
42
|
-
* @param {DonationPaymentInfo} donationInfo
|
|
43
|
-
* @param {DonorContactInfo} donorContactInfo
|
|
44
|
-
* @returns {Promise<void>}
|
|
45
|
-
* @memberof CreditCardFlowHandlerInterface
|
|
46
|
-
*/
|
|
47
|
-
paymentInitiated(
|
|
48
|
-
hostedFieldsResponse: braintree.HostedFieldsTokenizePayload,
|
|
49
|
-
donationInfo: DonationPaymentInfo,
|
|
50
|
-
donorContactInfo: DonorContactInfo,
|
|
51
|
-
): Promise<void>;
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Bind to receive credit card flow handler events
|
|
55
|
-
*
|
|
56
|
-
* @template E
|
|
57
|
-
* @param {E} event
|
|
58
|
-
* @param {CreditCardFlowHandlerEvents[E]} callback
|
|
59
|
-
* @returns {Unsubscribe}
|
|
60
|
-
* @memberof CreditCardFlowHandlerInterface
|
|
61
|
-
*/
|
|
62
|
-
on<E extends keyof CreditCardFlowHandlerEvents>(
|
|
63
|
-
event: E,
|
|
64
|
-
callback: CreditCardFlowHandlerEvents[E],
|
|
65
|
-
): Unsubscribe;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export interface CreditCardFlowHandlerEvents {
|
|
69
|
-
validityChanged: (isValid: boolean) => void;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export class CreditCardFlowHandler implements CreditCardFlowHandlerInterface {
|
|
73
|
-
private donationFlowModalManager: DonationFlowModalManagerInterface;
|
|
74
|
-
|
|
75
|
-
private braintreeManager: BraintreeManagerInterface;
|
|
76
|
-
|
|
77
|
-
private recaptchaManager: RecaptchaManagerInterface;
|
|
78
|
-
|
|
79
|
-
private emitter: Emitter<CreditCardFlowHandlerEvents>;
|
|
80
|
-
|
|
81
|
-
constructor(options: {
|
|
82
|
-
braintreeManager: BraintreeManagerInterface;
|
|
83
|
-
donationFlowModalManager: DonationFlowModalManagerInterface;
|
|
84
|
-
recaptchaManager: RecaptchaManagerInterface;
|
|
85
|
-
}) {
|
|
86
|
-
this.braintreeManager = options.braintreeManager;
|
|
87
|
-
this.donationFlowModalManager = options.donationFlowModalManager;
|
|
88
|
-
this.recaptchaManager = options.recaptchaManager;
|
|
89
|
-
this.emitter = createNanoEvents<CreditCardFlowHandlerEvents>();
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/** @inheritdoc */
|
|
93
|
-
on<E extends keyof CreditCardFlowHandlerEvents>(
|
|
94
|
-
event: E,
|
|
95
|
-
callback: CreditCardFlowHandlerEvents[E],
|
|
96
|
-
): Unsubscribe {
|
|
97
|
-
return this.emitter.on(event, callback);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
private started = false;
|
|
101
|
-
|
|
102
|
-
/** @inheritdoc */
|
|
103
|
-
async startup(): Promise<void> {
|
|
104
|
-
if (this.started) {
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
this.started = true;
|
|
108
|
-
|
|
109
|
-
const handler = await this.braintreeManager?.paymentProviders.creditCardHandler.get();
|
|
110
|
-
const instance = await handler?.instance.get();
|
|
111
|
-
|
|
112
|
-
// NOTE: The `focus` and `blur` callback logic must work in conjunction with
|
|
113
|
-
// the `HostedFieldContainer` class. We use the `HostedFieldContainer` for
|
|
114
|
-
// managing the hosted field error state in other parts of the form, but
|
|
115
|
-
// since we can only get event callbacks from the hosted fields like this,
|
|
116
|
-
// this has to operate independently and modify the CSS styles by itself
|
|
117
|
-
instance?.on('focus', (event: braintree.HostedFieldsStateObject): void => {
|
|
118
|
-
const { emittedBy, fields } = event;
|
|
119
|
-
const fieldInFocus = fields[emittedBy];
|
|
120
|
-
const { container } = fieldInFocus;
|
|
121
|
-
(container.parentElement as BadgedInput).error = false;
|
|
122
|
-
handler.hideErrorMessage();
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
instance?.on('blur', (event: braintree.HostedFieldsStateObject): void => {
|
|
126
|
-
const { emittedBy, fields } = event;
|
|
127
|
-
const fieldInFocus = fields[emittedBy];
|
|
128
|
-
const { container, isEmpty, isValid } = fieldInFocus;
|
|
129
|
-
if (isEmpty || !isValid) {
|
|
130
|
-
(container.parentElement as BadgedInput).error = true;
|
|
131
|
-
}
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
instance?.on('validityChange', (event: braintree.HostedFieldsStateObject): void => {
|
|
135
|
-
const { fields } = event;
|
|
136
|
-
const isValid = fields.cvv.isValid && fields.expirationDate.isValid && fields.number.isValid;
|
|
137
|
-
this.emitter.emit('validityChanged', isValid);
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/** @inheritdoc */
|
|
142
|
-
async tokenizeFields(): Promise<braintree.HostedFieldsTokenizePayload | undefined> {
|
|
143
|
-
let hostedFieldsResponse: braintree.HostedFieldsTokenizePayload | undefined;
|
|
144
|
-
|
|
145
|
-
const handler = await this.braintreeManager.paymentProviders.creditCardHandler.get();
|
|
146
|
-
|
|
147
|
-
try {
|
|
148
|
-
hostedFieldsResponse = await handler?.tokenizeHostedFields();
|
|
149
|
-
} catch (error) {
|
|
150
|
-
this.handleHostedFieldTokenizationError(error as BraintreeError);
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
return hostedFieldsResponse;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/** @inheritdoc */
|
|
158
|
-
async paymentInitiated(
|
|
159
|
-
hostedFieldsResponse: braintree.HostedFieldsTokenizePayload,
|
|
160
|
-
donationInfo: DonationPaymentInfo,
|
|
161
|
-
donorContactInfo: DonorContactInfo,
|
|
162
|
-
): Promise<void> {
|
|
163
|
-
let recaptchaToken: string | undefined;
|
|
164
|
-
|
|
165
|
-
try {
|
|
166
|
-
recaptchaToken = await this.recaptchaManager.execute();
|
|
167
|
-
} catch (error) {
|
|
168
|
-
this.donationFlowModalManager.showErrorModal({
|
|
169
|
-
message: `Recaptcha failure`,
|
|
170
|
-
});
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
this.donationFlowModalManager.startDonationSubmissionFlow({
|
|
175
|
-
nonce: hostedFieldsResponse.nonce,
|
|
176
|
-
paymentProvider: PaymentProvider.CreditCard,
|
|
177
|
-
recaptchaToken: recaptchaToken,
|
|
178
|
-
bin: hostedFieldsResponse.details.bin,
|
|
179
|
-
donationInfo: donationInfo,
|
|
180
|
-
customerInfo: donorContactInfo.customer,
|
|
181
|
-
billingInfo: donorContactInfo.billing,
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
private async handleHostedFieldTokenizationError(error: braintree.BraintreeError): Promise<void> {
|
|
186
|
-
const handler = await this.braintreeManager.paymentProviders.creditCardHandler.get();
|
|
187
|
-
|
|
188
|
-
handler.showErrorMessage();
|
|
189
|
-
|
|
190
|
-
switch (error.code) {
|
|
191
|
-
case 'HOSTED_FIELDS_FIELDS_EMPTY':
|
|
192
|
-
// occurs when none of the fields are filled in
|
|
193
|
-
handler.markFieldErrors([
|
|
194
|
-
HostedFieldName.Number,
|
|
195
|
-
HostedFieldName.CVV,
|
|
196
|
-
HostedFieldName.ExpirationDate,
|
|
197
|
-
]);
|
|
198
|
-
break;
|
|
199
|
-
case 'HOSTED_FIELDS_FIELDS_INVALID':
|
|
200
|
-
// occurs when certain fields do not pass client side validation
|
|
201
|
-
Object.keys(error.details.invalidFields).forEach(key => {
|
|
202
|
-
handler.markFieldErrors([key as HostedFieldName]);
|
|
203
|
-
});
|
|
204
|
-
break;
|
|
205
|
-
case 'HOSTED_FIELDS_TOKENIZATION_FAIL_ON_DUPLICATE':
|
|
206
|
-
// occurs when:
|
|
207
|
-
// * the client token used for client authorization was generated
|
|
208
|
-
// with a customer ID and the fail on duplicate payment method
|
|
209
|
-
// option is set to true
|
|
210
|
-
// * the card being tokenized has previously been vaulted (with any customer)
|
|
211
|
-
break;
|
|
212
|
-
case 'HOSTED_FIELDS_TOKENIZATION_CVV_VERIFICATION_FAILED':
|
|
213
|
-
// occurs when:
|
|
214
|
-
// * the client token used for client authorization was generated
|
|
215
|
-
// with a customer ID and the verify card option is set to true
|
|
216
|
-
// and you have credit card verification turned on in the Braintree
|
|
217
|
-
// control panel
|
|
218
|
-
// * the cvv does not pass verfication
|
|
219
|
-
handler.markFieldErrors([HostedFieldName.CVV]);
|
|
220
|
-
break;
|
|
221
|
-
case 'HOSTED_FIELDS_FAILED_TOKENIZATION':
|
|
222
|
-
// occurs for any other tokenization error on the server
|
|
223
|
-
break;
|
|
224
|
-
case 'HOSTED_FIELDS_TOKENIZATION_NETWORK_ERROR':
|
|
225
|
-
// occurs when the Braintree gateway cannot be contacted
|
|
226
|
-
break;
|
|
227
|
-
default:
|
|
228
|
-
// something else happened
|
|
229
|
-
break;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
}
|
|
1
|
+
import { createNanoEvents, Emitter, Unsubscribe } from 'nanoevents';
|
|
2
|
+
|
|
3
|
+
import { BraintreeManagerInterface } from '../../braintree-manager/braintree-interfaces';
|
|
4
|
+
import { RecaptchaManagerInterface } from '../../recaptcha-manager/recaptcha-manager';
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
DonorContactInfo,
|
|
8
|
+
DonationPaymentInfo,
|
|
9
|
+
PaymentProvider,
|
|
10
|
+
} from '@internetarchive/donation-form-data-models';
|
|
11
|
+
import { DonationFlowModalManagerInterface } from '../donation-flow-modal-manager';
|
|
12
|
+
import { HostedFieldName } from '../../braintree-manager/payment-providers/credit-card/hosted-field-container';
|
|
13
|
+
import { BadgedInput } from '../../form-elements/badged-input';
|
|
14
|
+
import { BraintreeError } from '../../@types/braintree-web';
|
|
15
|
+
|
|
16
|
+
export interface CreditCardFlowHandlerInterface {
|
|
17
|
+
/**
|
|
18
|
+
* Set up the hosted fields with event bindings
|
|
19
|
+
*
|
|
20
|
+
* @returns {Promise<void>}
|
|
21
|
+
* @memberof CreditCardFlowHandlerInterface
|
|
22
|
+
*/
|
|
23
|
+
startup(): Promise<void>;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Tokenize the hosted fields.
|
|
27
|
+
*
|
|
28
|
+
* This also performs validation so we can call this before initiating the payment to
|
|
29
|
+
* display any error messaging.
|
|
30
|
+
*
|
|
31
|
+
* @returns {(Promise<braintree.HostedFieldsTokenizePayload | undefined>)}
|
|
32
|
+
* @memberof CreditCardFlowHandlerInterface
|
|
33
|
+
*/
|
|
34
|
+
tokenizeFields(): Promise<braintree.HostedFieldsTokenizePayload | undefined>;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Initiate the payment flow.
|
|
38
|
+
*
|
|
39
|
+
* You must get the response from `tokenizeFields()` first before this to validate the hosted fields.
|
|
40
|
+
*
|
|
41
|
+
* @param {braintree.HostedFieldsTokenizePayload} hostedFieldsResponse
|
|
42
|
+
* @param {DonationPaymentInfo} donationInfo
|
|
43
|
+
* @param {DonorContactInfo} donorContactInfo
|
|
44
|
+
* @returns {Promise<void>}
|
|
45
|
+
* @memberof CreditCardFlowHandlerInterface
|
|
46
|
+
*/
|
|
47
|
+
paymentInitiated(
|
|
48
|
+
hostedFieldsResponse: braintree.HostedFieldsTokenizePayload,
|
|
49
|
+
donationInfo: DonationPaymentInfo,
|
|
50
|
+
donorContactInfo: DonorContactInfo,
|
|
51
|
+
): Promise<void>;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Bind to receive credit card flow handler events
|
|
55
|
+
*
|
|
56
|
+
* @template E
|
|
57
|
+
* @param {E} event
|
|
58
|
+
* @param {CreditCardFlowHandlerEvents[E]} callback
|
|
59
|
+
* @returns {Unsubscribe}
|
|
60
|
+
* @memberof CreditCardFlowHandlerInterface
|
|
61
|
+
*/
|
|
62
|
+
on<E extends keyof CreditCardFlowHandlerEvents>(
|
|
63
|
+
event: E,
|
|
64
|
+
callback: CreditCardFlowHandlerEvents[E],
|
|
65
|
+
): Unsubscribe;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface CreditCardFlowHandlerEvents {
|
|
69
|
+
validityChanged: (isValid: boolean) => void;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export class CreditCardFlowHandler implements CreditCardFlowHandlerInterface {
|
|
73
|
+
private donationFlowModalManager: DonationFlowModalManagerInterface;
|
|
74
|
+
|
|
75
|
+
private braintreeManager: BraintreeManagerInterface;
|
|
76
|
+
|
|
77
|
+
private recaptchaManager: RecaptchaManagerInterface;
|
|
78
|
+
|
|
79
|
+
private emitter: Emitter<CreditCardFlowHandlerEvents>;
|
|
80
|
+
|
|
81
|
+
constructor(options: {
|
|
82
|
+
braintreeManager: BraintreeManagerInterface;
|
|
83
|
+
donationFlowModalManager: DonationFlowModalManagerInterface;
|
|
84
|
+
recaptchaManager: RecaptchaManagerInterface;
|
|
85
|
+
}) {
|
|
86
|
+
this.braintreeManager = options.braintreeManager;
|
|
87
|
+
this.donationFlowModalManager = options.donationFlowModalManager;
|
|
88
|
+
this.recaptchaManager = options.recaptchaManager;
|
|
89
|
+
this.emitter = createNanoEvents<CreditCardFlowHandlerEvents>();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** @inheritdoc */
|
|
93
|
+
on<E extends keyof CreditCardFlowHandlerEvents>(
|
|
94
|
+
event: E,
|
|
95
|
+
callback: CreditCardFlowHandlerEvents[E],
|
|
96
|
+
): Unsubscribe {
|
|
97
|
+
return this.emitter.on(event, callback);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private started = false;
|
|
101
|
+
|
|
102
|
+
/** @inheritdoc */
|
|
103
|
+
async startup(): Promise<void> {
|
|
104
|
+
if (this.started) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
this.started = true;
|
|
108
|
+
|
|
109
|
+
const handler = await this.braintreeManager?.paymentProviders.creditCardHandler.get();
|
|
110
|
+
const instance = await handler?.instance.get();
|
|
111
|
+
|
|
112
|
+
// NOTE: The `focus` and `blur` callback logic must work in conjunction with
|
|
113
|
+
// the `HostedFieldContainer` class. We use the `HostedFieldContainer` for
|
|
114
|
+
// managing the hosted field error state in other parts of the form, but
|
|
115
|
+
// since we can only get event callbacks from the hosted fields like this,
|
|
116
|
+
// this has to operate independently and modify the CSS styles by itself
|
|
117
|
+
instance?.on('focus', (event: braintree.HostedFieldsStateObject): void => {
|
|
118
|
+
const { emittedBy, fields } = event;
|
|
119
|
+
const fieldInFocus = fields[emittedBy];
|
|
120
|
+
const { container } = fieldInFocus;
|
|
121
|
+
(container.parentElement as BadgedInput).error = false;
|
|
122
|
+
handler.hideErrorMessage();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
instance?.on('blur', (event: braintree.HostedFieldsStateObject): void => {
|
|
126
|
+
const { emittedBy, fields } = event;
|
|
127
|
+
const fieldInFocus = fields[emittedBy];
|
|
128
|
+
const { container, isEmpty, isValid } = fieldInFocus;
|
|
129
|
+
if (isEmpty || !isValid) {
|
|
130
|
+
(container.parentElement as BadgedInput).error = true;
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
instance?.on('validityChange', (event: braintree.HostedFieldsStateObject): void => {
|
|
135
|
+
const { fields } = event;
|
|
136
|
+
const isValid = fields.cvv.isValid && fields.expirationDate.isValid && fields.number.isValid;
|
|
137
|
+
this.emitter.emit('validityChanged', isValid);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** @inheritdoc */
|
|
142
|
+
async tokenizeFields(): Promise<braintree.HostedFieldsTokenizePayload | undefined> {
|
|
143
|
+
let hostedFieldsResponse: braintree.HostedFieldsTokenizePayload | undefined;
|
|
144
|
+
|
|
145
|
+
const handler = await this.braintreeManager.paymentProviders.creditCardHandler.get();
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
hostedFieldsResponse = await handler?.tokenizeHostedFields();
|
|
149
|
+
} catch (error) {
|
|
150
|
+
this.handleHostedFieldTokenizationError(error as BraintreeError);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return hostedFieldsResponse;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** @inheritdoc */
|
|
158
|
+
async paymentInitiated(
|
|
159
|
+
hostedFieldsResponse: braintree.HostedFieldsTokenizePayload,
|
|
160
|
+
donationInfo: DonationPaymentInfo,
|
|
161
|
+
donorContactInfo: DonorContactInfo,
|
|
162
|
+
): Promise<void> {
|
|
163
|
+
let recaptchaToken: string | undefined;
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
recaptchaToken = await this.recaptchaManager.execute();
|
|
167
|
+
} catch (error) {
|
|
168
|
+
this.donationFlowModalManager.showErrorModal({
|
|
169
|
+
message: `Recaptcha failure`,
|
|
170
|
+
});
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
this.donationFlowModalManager.startDonationSubmissionFlow({
|
|
175
|
+
nonce: hostedFieldsResponse.nonce,
|
|
176
|
+
paymentProvider: PaymentProvider.CreditCard,
|
|
177
|
+
recaptchaToken: recaptchaToken,
|
|
178
|
+
bin: hostedFieldsResponse.details.bin,
|
|
179
|
+
donationInfo: donationInfo,
|
|
180
|
+
customerInfo: donorContactInfo.customer,
|
|
181
|
+
billingInfo: donorContactInfo.billing,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
private async handleHostedFieldTokenizationError(error: braintree.BraintreeError): Promise<void> {
|
|
186
|
+
const handler = await this.braintreeManager.paymentProviders.creditCardHandler.get();
|
|
187
|
+
|
|
188
|
+
handler.showErrorMessage();
|
|
189
|
+
|
|
190
|
+
switch (error.code) {
|
|
191
|
+
case 'HOSTED_FIELDS_FIELDS_EMPTY':
|
|
192
|
+
// occurs when none of the fields are filled in
|
|
193
|
+
handler.markFieldErrors([
|
|
194
|
+
HostedFieldName.Number,
|
|
195
|
+
HostedFieldName.CVV,
|
|
196
|
+
HostedFieldName.ExpirationDate,
|
|
197
|
+
]);
|
|
198
|
+
break;
|
|
199
|
+
case 'HOSTED_FIELDS_FIELDS_INVALID':
|
|
200
|
+
// occurs when certain fields do not pass client side validation
|
|
201
|
+
Object.keys(error.details.invalidFields).forEach(key => {
|
|
202
|
+
handler.markFieldErrors([key as HostedFieldName]);
|
|
203
|
+
});
|
|
204
|
+
break;
|
|
205
|
+
case 'HOSTED_FIELDS_TOKENIZATION_FAIL_ON_DUPLICATE':
|
|
206
|
+
// occurs when:
|
|
207
|
+
// * the client token used for client authorization was generated
|
|
208
|
+
// with a customer ID and the fail on duplicate payment method
|
|
209
|
+
// option is set to true
|
|
210
|
+
// * the card being tokenized has previously been vaulted (with any customer)
|
|
211
|
+
break;
|
|
212
|
+
case 'HOSTED_FIELDS_TOKENIZATION_CVV_VERIFICATION_FAILED':
|
|
213
|
+
// occurs when:
|
|
214
|
+
// * the client token used for client authorization was generated
|
|
215
|
+
// with a customer ID and the verify card option is set to true
|
|
216
|
+
// and you have credit card verification turned on in the Braintree
|
|
217
|
+
// control panel
|
|
218
|
+
// * the cvv does not pass verfication
|
|
219
|
+
handler.markFieldErrors([HostedFieldName.CVV]);
|
|
220
|
+
break;
|
|
221
|
+
case 'HOSTED_FIELDS_FAILED_TOKENIZATION':
|
|
222
|
+
// occurs for any other tokenization error on the server
|
|
223
|
+
break;
|
|
224
|
+
case 'HOSTED_FIELDS_TOKENIZATION_NETWORK_ERROR':
|
|
225
|
+
// occurs when the Braintree gateway cannot be contacted
|
|
226
|
+
break;
|
|
227
|
+
default:
|
|
228
|
+
// something else happened
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|