diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bfc8db..14a03b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,44 @@ `react-stripe-elements` adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## v6.0.0 - 2019-11-05 + +### New Features + +- `injectStripe` now injects a reference to the Elements instance created by + `` as the prop `elements`. + +The primary reason you would want an Elements instance is to use +[`elements.getElement()`](https://stripe.com/docs/stripe-js/reference#elements-get-element). +which provides an easy way to get a reference to an Element. You will need to +get a refrence to an Element to use +[`confirmCardPayment`](https://stripe.com/docs/stripe-js/reference#stripe-confirm-card-payment), +[`confirmCardSetup()`](https://stripe.com/docs/stripe-js/reference#stripe-confirm-card-setup), +or +[`createPaymentMethod()`](https://stripe.com/docs/stripe-js/reference#stripe-create-payment-method). + +Note that the old API for `createPaymentMethod` will continue to work and +provide automatic element injection, but we are updating documentation and +examples to use the new argument shape: + +```js +// old shape with automatic element detection - still works +this.props.stripe.createPaymentMethod('card').then(/* ... */); + +// new shape without automatic element detection - recommended and will work with new non-card PaymentMethods +this.props.stripe + .createPaymentMethod({ + type: 'card', + card: this.props.elements.getElement('card'), + }) + .then(/* ... */); +``` + +### Breaking Changes + +- We have removed the `getElement` method on RSE components that we introduced + in v5.1.0 in favor of the above change. Sorry for the churn. + ## v5.1.0 - 2019-10-22 ### New Features @@ -53,9 +91,11 @@ ): Promise<{error?: Object, setupIntent?: Object}> ``` - For more information, please review the Stripe Docs: +```` + +For more information, please review the Stripe Docs: - - [`stripe.handleCardSetup`](https://stripe.com/docs/stripe-js/reference#stripe-handle-card-setup) +- [`stripe.handleCardSetup`](https://stripe.com/docs/stripe-js/reference#stripe-handle-card-setup) ### Deprecations @@ -367,3 +407,4 @@ Initial release! Support for: - CardExpiryElement - CardCVCElement - PostalCodeElement +```` diff --git a/README.md b/README.md index 8c3654c..63b39f0 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,6 @@ styles, fonts). - [Available components](#available-components) - [Props shape](#props-shape-2) - [Using `onReady`](#using-onready) - - [Using `getElement`](#using-getelement) - [`injectStripe` HOC](#injectstripe-hoc) - [Example](#example) - [Troubleshooting](#troubleshooting) @@ -167,19 +166,13 @@ Use the `injectStripe` [Higher-Order Component][hoc] (HOC) to build your payment form components in the `Elements` tree. The [Higher-Order Component][hoc] pattern in React can be unfamiliar to those who've never seen it before, so consider reading up before continuing. The `injectStripe` HOC provides the -`this.props.stripe` property that manages your `Elements` groups. Within an -injected component, you can call any of the following: - -- `this.props.stripe.createPaymentMethod` -- `this.props.stripe.createToken` -- `this.props.stripe.createSource` -- `this.props.stripe.handleCardPayment` -- `this.props.stripe.handleCardSetup` - -Calling any of these methods will collect data from the appropriate Element and -use it to submit payment data to Stripe. +`this.props.stripe` and `this.props.elements` properties that manage your +`Elements` groups. Within an injected component, you can call any of the methods +on the [Stripe][stripe] or [Elements][elements] objects. [hoc]: https://facebook.github.io/react/docs/higher-order-components.html +[stripe]: https://stripe.com/docs/stripe-js/reference#the-stripe-object +[elements]: https://stripe.com/docs/stripe-js/reference#the-elements-object > :warning: NOTE `injectStripe` cannot be used on the same element that renders > the `Elements` component; it must be used on the child component of @@ -200,29 +193,49 @@ class CheckoutForm extends React.Component { // We don't want to let default form submission happen here, which would refresh the page. ev.preventDefault(); - // Within the context of `Elements`, this call to createPaymentMethod knows from which Element to - // create the PaymentMethod, since there's only one in this group. + // Use Elements to get a reference to the Card Element mounted somewhere + // in your tree. Elements will know how to find your Card Element + // becase only one is allowed. + // See our getElement documentation for more: + // https://stripe.com/docs/stripe-js/reference#elements-get-element + const cardElement = this.props.elements.getElement('card'); + + // From here we cal call createPaymentMethod to create a PaymentMethod // See our createPaymentMethod documentation for more: // https://stripe.com/docs/stripe-js/reference#stripe-create-payment-method this.props.stripe - .createPaymentMethod('card', {billing_details: {name: 'Jenny Rosen'}}) + .createPaymentMethod({ + type: 'card', + card: cardElement, + billing_details: {name: 'Jenny Rosen'}, + }) .then(({paymentMethod}) => { console.log('Received Stripe PaymentMethod:', paymentMethod); }); - // You can also use handleCardPayment with the PaymentIntents API automatic confirmation flow. - // See our handleCardPayment documentation for more: - // https://stripe.com/docs/stripe-js/reference#stripe-handle-card-payment - this.props.stripe.handleCardPayment('{PAYMENT_INTENT_CLIENT_SECRET}', data); + // You can also use confirmCardPayment with the PaymentIntents API automatic confirmation flow. + // See our confirmCardPayment documentation for more: + // https://stripe.com/docs/stripe-js/reference#stripe-confirm-card-payment + this.props.stripe.confirmCardPayment('{PAYMENT_INTENT_CLIENT_SECRET}', { + payment_method: { + card: cardElement, + }, + }); - // You can also use handleCardSetup with the SetupIntents API. - // See our handleCardSetup documentation for more: - // https://stripe.com/docs/stripe-js/reference#stripe-handle-card-setup - this.props.stripe.handleCardSetup('{PAYMENT_INTENT_CLIENT_SECRET}', data); + // You can also use confirmCardSetup with the SetupIntents API. + // See our confirmCardSetup documentation for more: + // https://stripe.com/docs/stripe-js/reference#stripe-confirm-card-setup + this.props.stripe.confirmCardSetpu('{PAYMENT_INTENT_CLIENT_SECRET}', { + payment_method: { + card: cardElement, + }, + }); // You can also use createToken to create tokens. // See our tokens documentation for more: // https://stripe.com/docs/stripe-js/reference#stripe-create-token + // With createToken, you will not need to pass in the reference to + // the Element. It will be inferred automatically. this.props.stripe.createToken({type: 'card', name: 'Jenny Rosen'}); // token type can optionally be inferred if there is only one Element // with which to create tokens @@ -231,6 +244,8 @@ class CheckoutForm extends React.Component { // You can also use createSource to create Sources. // See our Sources documentation for more: // https://stripe.com/docs/stripe-js/reference#stripe-create-source + // With createSource, you will not need to pass in the reference to + // the Element. It will be inferred automatically. this.props.stripe.createSource({ type: 'card', owner: { @@ -640,45 +655,6 @@ export default CardSection; [element]: https://stripe.com/docs/stripe-js/reference#other-methods -#### Using `getElement` - -In addition to `onReady`, you can also access to the underlying Element by using -a [ref] to the access the component instance and calling `getElement()`. - -Note that if you are async loading Stripe.js, until it loads `getElement` will -return `null`. - -```jsx -// CardSection.js -import React from 'react'; -import {CardElement} from 'react-stripe-elements'; - -class CardSection extends React.Component { - ref = React.createRef(); - - focus() { - const element = this.ref.current.getElement(); - - if (element) { - element.focus(); - } - } - - render = () => { - return ( - - ); - }; -} - -export default CardSection; -``` - -[ref]: https://reactjs.org/docs/refs-and-the-dom.html - ### `injectStripe` HOC ```jsx @@ -693,14 +669,13 @@ function injectStripe( Use `injectStripe` to wrap a component that needs to interact with `Stripe.js` to create sources or tokens. -1. First, create a component that accepts the `stripe` prop and calls - `this.props.stripe.createToken` or `this.props.stripe.createSource` when - necessary. +1. First, create a component that accepts the `stripe` prop and calls one of + the Stripe or Elements methods when necessary. 2. Wrap that component by passing it to `injectStripe` so that it actually - receives the `stripe` prop. + receives the `stripe` and `elements` props. 3. Render the component that `injectStripe` returns. -### Example +#### Example ```jsx // 1. Create a component that uses this.props.stripe: @@ -709,7 +684,12 @@ class CheckoutForm extends React.Component { /* ... */ } onCompleteCheckout() { - this.props.stripe.createPaymentMethod('card').then(/* ... */); + this.props.stripe + .createPaymentMethod({ + type: 'card', + card: this.props.stripe.getElement('card'), + }) + .then(/* ... */); } } @@ -740,10 +720,16 @@ available with the `getWrappedInstance()` method of the wrapper component. This feature can not be used if the wrapped component is a stateless function component. -Within the wrapped component, the `stripe` prop has the type: +Within the wrapped component, the `stripe` and `elements` props have the type: ```jsx type FactoryProps = { + elements: null | { + getElement: (type: string) => Element | null, + // For more detail and documentation on other methods available on + // the `elements` object, please refer to our official documentation: + // https://stripe.com/docs/elements/reference#the-elements-object + }, stripe: null | { createToken: (tokenData: {type?: string}) => Promise<{ token?: Object, @@ -754,36 +740,36 @@ type FactoryProps = { error?: Object, }>, createPaymentMethod: ( - type: string, - paymentMethodData?: Object + paymentMethodData: Object ) => Promise<{ paymentMethod?: Object, error?: Object, }>, - handleCardPayment: ( + confirmCardPayment: ( clientSecret: string, - paymentMethodData?: Object + paymentIntentData?: Object ) => Promise<{ paymentIntent?: Object, error?: Object, }>, - handleCardSetup: ( + confirmCardSetup: ( clientSecret: string, - paymentMethodData?: Object + paymentIntentData?: Object ) => Promise<{ setupIntent?: Object, error?: Object, }>, - // and other functions available on the `stripe` object, - // as officially documented here: https://stripe.com/docs/elements/reference#the-stripe-object + // For more detail and documentation on other methods available on + // the `stripe` object, please refer to our official documentation: + // https://stripe.com/docs/elements/reference#the-stripe-object }, }; ``` -The `stripe` prop can only be `null` if you are using one of the +The `stripe` and `elements` props can only be `null` if you are using one of the [Advanced integrations](#advanced-integrations) mentioned above, like loading Stripe.js asynchronously or providing an existing instance. If you are using a -basic integration where you pass in an api key to ``, it will +basic integration where you pass in an api key to ``, they will always be present. ## Troubleshooting diff --git a/demo/intents/index.js b/demo/intents/index.js index 23439f4..c6a8edb 100644 --- a/demo/intents/index.js +++ b/demo/intents/index.js @@ -63,22 +63,27 @@ class _CreatePaymentMethod extends React.Component< handleSubmit = (ev) => { ev.preventDefault(); - if (this.props.stripe) { - this.props.stripe.createPaymentMethod('card').then((payload) => { - if (payload.error) { - this.setState({ - error: `Failed to create PaymentMethod: ${payload.error.message}`, - processing: false, - }); - console.log('[error]', payload.error); - } else { - this.setState({ - message: `Created PaymentMethod: ${payload.paymentMethod.id}`, - processing: false, - }); - console.log('[paymentMethod]', payload.paymentMethod); - } - }); + if (this.props.stripe && this.props.elements) { + this.props.stripe + .createPaymentMethod({ + type: 'card', + card: this.props.elements.getElement('card'), + }) + .then((payload) => { + if (payload.error) { + this.setState({ + error: `Failed to create PaymentMethod: ${payload.error.message}`, + processing: false, + }); + console.log('[error]', payload.error); + } else { + this.setState({ + message: `Created PaymentMethod: ${payload.paymentMethod.id}`, + processing: false, + }); + console.log('[paymentMethod]', payload.paymentMethod); + } + }); this.setState({processing: true}); } else { console.log("Stripe.js hasn't loaded yet."); @@ -149,9 +154,13 @@ class _HandleCardPayment extends React.Component< handleSubmit = (ev) => { ev.preventDefault(); - if (this.props.stripe) { + if (this.props.stripe && this.props.elements) { this.props.stripe - .handleCardPayment(this.state.clientSecret) + .confirmCardPayment(this.state.clientSecret, { + payment_method: { + card: this.props.elements.getElement('card'), + }, + }) .then((payload) => { if (payload.error) { this.setState({ @@ -179,7 +188,7 @@ class _HandleCardPayment extends React.Component< return (