diff --git a/lib/three-d-secure-action.jsx b/lib/three-d-secure-action.jsx
index de8c7ab..37bfc40 100644
--- a/lib/three-d-secure-action.jsx
+++ b/lib/three-d-secure-action.jsx
@@ -20,6 +20,16 @@ export default class ThreeDSecureAction extends React.PureComponent {
*/
actionTokenId: PropTypes.string,
+ /**
+ * Called when the 3-D Secure flow is ready for interaction
+ * @type {ThreeDSecureAction~onReady}
+ */
+
+ /**
+ * @callback ThreeDSecureAction~onReady
+ */
+ onReady: PropTypes.func,
+
/**
* Called when the user has completed the 3D Secure flow
* @type {ThreeDSecureAction~onToken}
@@ -47,6 +57,7 @@ export default class ThreeDSecureAction extends React.PureComponent {
id: undefined,
className: undefined,
actionTokenId: '',
+ onReady: () => {},
onToken: () => {},
onError: e => { throw e }
};
@@ -62,15 +73,19 @@ export default class ThreeDSecureAction extends React.PureComponent {
const { actionTokenId } = props;
+ this._attached = false;
this._container = React.createRef();
this._risk = this.context.recurly.Risk();
this._threeDSecure = this._risk.ThreeDSecure({ actionTokenId });
+ this._threeDSecure.on('ready', (...args) => this.props.onReady(...args));
this._threeDSecure.on('token', (...args) => this.props.onToken(...args));
this._threeDSecure.on('error', (...args) => this.props.onError(...args));
}
componentDidMount () {
+ if (this._attached) return;
this._threeDSecure.attach(this._container.current);
+ this._attached = true;
}
render () {
diff --git a/test/three-d-secure-action.test.jsx b/test/three-d-secure-action.test.jsx
index 60dc31e..d4d0928 100644
--- a/test/three-d-secure-action.test.jsx
+++ b/test/three-d-secure-action.test.jsx
@@ -45,6 +45,31 @@ describe('', function () {
describe('event handlers', function () {
const example = { arbitrary: 'properties' };
+ describe('[onReady]', function () {
+ it('is called when the underlying ThreeDSecure instance is ready', function () {
+ const subject = jest.fn();
+ let fixture;
+
+ render(withRecurlyProvider(
+ fixture = ref}
+ />
+ ));
+
+ const instance = getThreeDSecureInstanceFrom(fixture);
+
+ // stub strategy so that the readiness chain will not fall apart
+ instance.strategy = {
+ attach: () => {}
+ };
+
+ instance.emit('ready');
+ expect(subject).toHaveBeenCalled();
+ });
+ });
+
describe('[onToken]', function () {
it('is called when the underlying ThreeDSecure instance receives a token', function () {
const subject = jest.fn();
diff --git a/types/index.d.ts b/types/index.d.ts
index e46a1a3..e0407c5 100644
--- a/types/index.d.ts
+++ b/types/index.d.ts
@@ -117,6 +117,7 @@ export type ThreeDSecureActionProps = {
id?: string;
className?: string;
actionTokenId?: string;
+ onReady?: () => void;
onToken?: (token: TokenPayload) => void;
onError?: (e: RecurlyError) => void;
};