Coder Social home page Coder Social logo

react-payment-inputs's Introduction

React Payment Inputs

A React Hook & Container to help with payment card input fields.

Requirements

Ensure you are running on a hooks-compatible version of React (v16.8 & above).

Installation

npm install react-payment-inputs --save

or install with Yarn if you prefer:

yarn add react-payment-inputs

Usage

By default (as seen above), React Payment Inputs does not come with built-in styling meaning that you can easily adapt React Payment Inputs to your own design system.

However, if you would like to use the built-in styles as seen in the animation above, read "Using the built-in styled wrapper".

With hooks

If you'd like to use the hooks version of React Payment Inputs, you can import usePaymentInputs into your component.

import React from 'react';
import { usePaymentInputs } from 'react-payment-inputs';

export default function PaymentInputs() {
  const { meta, getCardNumberProps, getExpiryDateProps, getCVCProps } = usePaymentInputs();

  return (
    <div>
      <input {...getCardNumberProps({ onChange: handleChangeCardNumber })} value={cardNumber} />
      <input {...getExpiryDateProps({ onChange: handleChangeExpiryDate })} value={expiryDate} />
      <input {...getCVCProps({ onChange: handleChangeCVC })} value={cvc} />
      {meta.isTouched && meta.error && <span>Error: {meta.error}</span>}
    </div>
  );
}

By spreading the prop getter functions (e.g. {...getCardNumberProps()}) on the inputs as shown above, React Payment Inputs will automatically handle the formatting, focus & validation logic for you.

IMPORTANT: You must place your event handlers (e.g. onChange, onBlur, etc) inside the prop getter function (e.g. getCardNumberProps()) so the default event handlers in React Payment Inputs don't get overridden.

With render props

If you'd like to use the render props version of React Payment Inputs, you can import PaymentInputsContainer into your component.

The props of <PaymentInputsContainer> are the same as the hook options and the render props are the same as the hook data.

import React from 'react';
import { PaymentInputsContainer } from 'react-payment-inputs';

export default function PaymentInputs() {
  return (
    <PaymentInputsContainer>
      {({ meta, getCardNumberProps, getExpiryDateProps, getCVCProps }) => (
        <div>
          <input {...getCardNumberProps({ onChange: handleChangeCardNumber })} value={cardNumber} />
          <input {...getExpiryDateProps({ onChange: handleChangeExpiryDate })} value={expiryDate} />
          <input {...getCVCProps({ onChange: handleChangeCVC })} value={cvc} />
          {meta.isTouched && meta.error && <span>Error: {meta.error}</span>}
        </div>
      )}
    </PaymentInputsContainer>
  );
}

IMPORTANT: You must place your event handlers (e.g. onChange, onBlur, etc) inside the prop getter function (e.g. getCardNumberProps()) so the default event handlers in React Payment Inputs don't get overridden.

Using the built-in styled wrapper

Note: <PaymentInputsWrapper> requires styled-components to be installed as a dependency.

By default, React Payment Inputs does not have built-in styling for it's inputs. However, React Payment Inputs comes with a styled wrapper which combines the card number, expiry & CVC fields seen below:

import React from 'react';
import { PaymentInputsWrapper, usePaymentInputs } from 'react-payment-inputs';
import images from 'react-payment-inputs/images';

export default function PaymentInputs() {
  const {
    wrapperProps,
    getCardImageProps,
    getCardNumberProps,
    getExpiryDateProps,
    getCVCProps
  } = usePaymentInputs();

  return (
    <PaymentInputsWrapper {...wrapperProps}>
      <svg {...getCardImageProps({ images })} />
      <input {...getCardNumberProps()} />
      <input {...getExpiryDateProps()} />
      <input {...getCVCProps()} />
    </PaymentInputsWrapper>
  );
}

More examples

data = usePaymentInputs(options)

returns an object (data)

options

Object({ cardNumberValidator, cvcValidator, errorMessages, expiryValidator, onBlur, onChange, onError, onTouch })

options.cardNumberValidator

function({cardNumber, cardType, errorMessages})

Set custom card number validator function

Example
const cardNumberValidator = ({ cardNumber, cardType, errorMessages }) => {
  if (cardType.displayName === 'Visa' || cardType.displayName === 'Mastercard') {
    return;
  }
  return 'Card must be Visa or Mastercard';
}

export default function MyComponent() {
  const { ... } = usePaymentInputs({
    cardNumberValidator
  });
}

options.cvcValidator

function({cvc, cardType, errorMessages})

Set custom cvc validator function

options.errorMessages

Object

Set custom error messages for the inputs.

Example
const ERROR_MESSAGES = {
  emptyCardNumber: 'El número de la tarjeta es inválido',
  invalidCardNumber: 'El número de la tarjeta es inválido',
  emptyExpiryDate: 'La fecha de expiración es inválida',
  monthOutOfRange: 'El mes de expiración debe estar entre 01 y 12',
  yearOutOfRange: 'El año de expiración no puede estar en el pasado',
  dateOutOfRange: 'La fecha de expiración no puede estar en el pasado',
  invalidExpiryDate: 'La fecha de expiración es inválida',
  emptyCVC: 'El código de seguridad es inválido',
  invalidCVC: 'El código de seguridad es inválido'
}

export default function MyComponent() {
  const { ... } = usePaymentInputs({
    errorMessages: ERROR_MESSAGES
  });
}

options.expiryDateValidator

function({expiryDate, errorMessages})

Set custom expiry date validator function

options.onBlur

function(event)

Function to handle the blur event on the inputs. It is invoked when any of the inputs blur.

options.onChange

function(event)

Function to handle the change event on the inputs. It is invoked when any of the inputs change.

options.onError

function(error, erroredInputs)

Function to invoke when any of the inputs error.

options.onTouch

function(touchedInput, touchedInputs)

Function to invoke when any of the inputs are touched.

data

getCardNumberProps

function(overrideProps) | returns Object<props>

Returns the props to apply to the card number input.

IMPORTANT: You must place your event handlers (e.g. onChange, onBlur, etc) inside the getCardNumberProps() so the default event handlers in React Payment Inputs don't get overridden.

Example snippet
<input {...getCardNumberProps({ onBlur: handleBlur, onChange: handleChange })} />

getExpiryDateProps

function(overrideProps) | returns Object<props>

Returns the props to apply to the expiry date input.

IMPORTANT: You must place your event handlers (e.g. onChange, onBlur, etc) inside the getExpiryDateProps() so the default event handlers in React Payment Inputs don't get overridden.

Example snippet
<input {...getExpiryDateProps({ onBlur: handleBlur, onChange: handleChange })} />

getCVCProps

function(overrideProps) | returns Object<props>

Returns the props to apply to the CVC input.

IMPORTANT: You must place your event handlers (e.g. onChange, onBlur, etc) inside the getCVCProps() so the default event handlers in React Payment Inputs don't get overridden.

Example snippet
<input {...getCVCProps({ onBlur: handleBlur, onChange: handleChange })} />

getZIPProps

function(overrideProps) | returns Object<props>

Returns the props to apply to the ZIP input.

IMPORTANT: You must place your event handlers (e.g. onChange, onBlur, etc) inside the getZIPProps() so the default event handlers in React Payment Inputs don't get overridden.

Example snippet
<input {...getZIPProps({ onBlur: handleBlur, onChange: handleChange })} />

getCardImageProps

function({ images }) | returns Object<props>

Returns the props to apply to the card image SVG.

This function only supports SVG elements currently. If you have a need for another format, please raise an issue.

You can also supply custom card images using the images attribute. The example below uses the default card images from React Payment Inputs.

Example snippet
import images from 'react-payment-inputs/images';

<svg {...getCardImageProps({ images })} />

meta.cardType

Object

Returns information about the current card type, including: name, lengths and formats.

Example snippet
const { meta } = usePaymentInputs();

<span>Current card: {meta.cardType.displayName}</span>

meta.error

string

Returns the current global error between all rendered inputs.

Example snippet
const { meta } = usePaymentInputs();

console.log(meta.error); // "Card number is invalid"

meta.isTouched

boolean

Returns the current global touched state between all rendered inputs.

meta.erroredInputs

Object

Returns the error message of each rendered input.

Example snippet
const { meta } = usePaymentInputs();

console.log(meta.erroredInputs);
/*
{
  cardNumber: undefined,
  expiryDate: 'Enter an expiry date',
  cvc: 'Enter a CVC'
}
*/

meta.touchedInputs

Object

Returns the touch state of each rendered input.

Example snippet
const { meta } = usePaymentInputs();

console.log(meta.touchedInputs);
/*
{
  cardNumber: true,
  expiryDate: true,
  cvc: false
}
*/

meta.focused

string

Returns the current focused input.

const { meta } = usePaymentInputs();

console.log(meta.focused); // "cardNumber"

wrapperProps

Object

Returns the props to apply to <PaymentInputsWrapper>.

<PaymentInputsWrapper> props

styles

Object

Custom styling to pass through to the wrapper. Either a styled-component's css or an Object can be passed.

Schema

{
  fieldWrapper: {
    base: css | Object,
    errored: css | Object
  },
  inputWrapper: {
    base: css | Object,
    errored: css | Object,
    focused: css | Object
  },
  input: {
    base: css | Object,
    errored: css | Object,
    cardNumber: css | Object,
    expiryDate: css | Object,
    cvc: css | Object
  },
  errorText: {
    base: css | Object
  }
}

errorTextProps

Object

Custom props to pass to the error text component.

inputWrapperProps

Object

Custom props to pass to the input wrapper component.

Using a third-party UI library

React Payment Inputs allows you to integrate into pretty much any React UI library. Below are a couple of examples of how you can fit React Payment Inputs into a UI library using usePaymentInputs. You can also do the same with <PaymentInputsContainer>.

Fannypack

import React from 'react';
import { FieldSet, InputField } from 'fannypack';
import { usePaymentInputs } from 'react-payment-inputs';
import images from 'react-payment-inputs/images';

export default function PaymentInputs() {
  const {
    meta,
    getCardNumberProps,
    getExpiryDateProps,
    getCVCProps
  } = usePaymentInputs();
  const { erroredInputs, touchedInputs } = meta;

  return (
    <FieldSet isHorizontal>
      <InputField
        // Here is where React Payment Inputs injects itself into the input element.
        {...getCardNumberProps()}
        placeholder="0000 0000 0000 0000"
        label="Card number"
        inputRef={getCardNumberProps().ref}
        // You can retrieve error state by making use of the error & touched attributes in `meta`.
        state={erroredInputs.cardNumber && touchedInputs.cardNumber ? 'danger' : undefined}
        validationText={touchedInputs.cardNumber && erroredInputs.cardNumber}
        maxWidth="15rem"
      />
      <InputField
        {...getExpiryDateProps()}
        label="Expiry date"
        inputRef={getExpiryDateProps().ref}
        state={erroredInputs.expiryDate && touchedInputs.expiryDate ? 'danger' : undefined}
        validationText={touchedInputs.expiryDate && erroredInputs.expiryDate}
        maxWidth="8rem"
      />
      <InputField
        {...getCVCProps()}
        placeholder="123"
        label="CVC"
        inputRef={getCVCProps().ref}
        state={erroredInputs.cvc && touchedInputs.cvc ? 'danger' : undefined}
        validationText={touchedInputs.cvc && erroredInputs.cvc}
        maxWidth="5rem"
      />
    </FieldSet>
  );
}

Bootstrap

import React from 'react';
import { FieldSet, InputField } from 'fannypack';
import { usePaymentInputs } from 'react-payment-inputs';
import images from 'react-payment-inputs/images';

export default function PaymentInputs() {
  const {
    meta,
    getCardNumberProps,
    getExpiryDateProps,
    getCVCProps
  } = usePaymentInputs();
  const { erroredInputs, touchedInputs } = meta;

  return (
    <Form>
      <Form.Row>
        <Form.Group as={Col} style={{ maxWidth: '15rem' }}>
          <Form.Label>Card number</Form.Label>
          <Form.Control
            // Here is where React Payment Inputs injects itself into the input element.
            {...getCardNumberProps()}
            // You can retrieve error state by making use of the error & touched attributes in `meta`.
            isInvalid={touchedInputs.cardNumber && erroredInputs.cardNumber}
            placeholder="0000 0000 0000 0000"
          />
          <Form.Control.Feedback type="invalid">{erroredInputs.cardNumber}</Form.Control.Feedback>
        </Form.Group>
        <Form.Group as={Col} style={{ maxWidth: '10rem' }}>
          <Form.Label>Expiry date</Form.Label>
          <Form.Control
            {...getExpiryDateProps()}
            isInvalid={touchedInputs.expiryDate && erroredInputs.expiryDate}
          />
          <Form.Control.Feedback type="invalid">{erroredInputs.expiryDate}</Form.Control.Feedback>
        </Form.Group>
        <Form.Group as={Col} style={{ maxWidth: '7rem' }}>
          <Form.Label>CVC</Form.Label>
          <Form.Control
            {...getCVCProps()}
            isInvalid={touchedInputs.cvc && erroredInputs.cvc}
            placeholder="123"
          />
          <Form.Control.Feedback type="invalid">{erroredInputs.cvc}</Form.Control.Feedback>
        </Form.Group>
      </Form.Row>
    </Form>
  );
}

Form library examples

React Payment Inputs has support for any type of React form library. Below are examples using Formik & React Final Form.

Formik

import { Formik, Field } from 'formik';
import { PaymentInputsWrapper, usePaymentInputs } from 'react-payment-inputs';

function PaymentForm() {
  const {
    meta,
    getCardImageProps,
    getCardNumberProps,
    getExpiryDateProps,
    getCVCProps,
    wrapperProps
  } = usePaymentInputs();

  return (
    <Formik
      initialValues={{
        cardNumber: '',
        expiryDate: '',
        cvc: ''
      }}
      onSubmit={data => console.log(data)}
      validate={() => {
        let errors = {};
        if (meta.erroredInputs.cardNumber) {
          errors.cardNumber = meta.erroredInputs.cardNumber;
        }
        if (meta.erroredInputs.expiryDate) {
          errors.expiryDate = meta.erroredInputs.expiryDate;
        }
        if (meta.erroredInputs.cvc) {
          errors.cvc = meta.erroredInputs.cvc;
        }
        return errors;
      }}
    >
      {({ handleSubmit }) => (
        <form onSubmit={handleSubmit}>
          <div>
            <PaymentInputsWrapper {...wrapperProps}>
              <svg {...getCardImageProps({ images })} />
              <Field name="cardNumber">
                {({ field }) => (
                  <input {...getCardNumberProps({ onBlur: field.onBlur, onChange: field.onChange })} />
                )}
              </Field>
              <Field name="expiryDate">
                {({ field }) => (
                  <input {...getExpiryDateProps({ onBlur: field.onBlur, onChange: field.onChange })} />
                )}
              </Field>
              <Field name="cvc">
                {({ field }) => <input {...getCVCProps({ onBlur: field.onBlur, onChange: field.onChange })} />}
              </Field>
            </PaymentInputsWrapper>
          </div>
          <Button marginTop="major-2" type="submit">
            Submit
          </Button>
        </form>
      )}
    </Formik>
  );
}

See this example in Storybook

React Final Form

import { Form, Field } from 'react-final-form';
import { PaymentInputsWrapper, usePaymentInputs } from 'react-payment-inputs';

function PaymentForm() {
  const {
    meta,
    getCardImageProps,
    getCardNumberProps,
    getExpiryDateProps,
    getCVCProps,
    wrapperProps
  } = usePaymentInputs();

  return (
    <Form
      onSubmit={data => console.log(data)}
      validate={() => {
        let errors = {};
        if (meta.erroredInputs.cardNumber) {
          errors.cardNumber = meta.erroredInputs.cardNumber;
        }
        if (meta.erroredInputs.expiryDate) {
          errors.expiryDate = meta.erroredInputs.expiryDate;
        }
        if (meta.erroredInputs.cvc) {
          errors.cvc = meta.erroredInputs.cvc;
        }
        return errors;
      }}
    >
      {({ handleSubmit }) => (
        <form onSubmit={handleSubmit}>
          <div>
            <PaymentInputsWrapper {...wrapperProps}>
              <svg {...getCardImageProps({ images })} />
              <Field name="cardNumber">
                {({ input }) => (
                  <input {...getCardNumberProps({ onBlur: input.onBlur, onChange: input.onChange })} />
                )}
              </Field>
              <Field name="expiryDate">
                {({ input }) => (
                  <input {...getExpiryDateProps({ onBlur: input.onBlur, onChange: input.onChange })} />
                )}
              </Field>
              <Field name="cvc">
                {({ input }) => <input {...getCVCProps({ onBlur: input.onBlur, onChange: input.onChange })} />}
              </Field>
            </PaymentInputsWrapper>
          </div>
          <Button marginTop="major-2" type="submit">
            Submit
          </Button>
        </form>
      )}
    </Form>
  );
}

See this example in Storybook

Customising the in-built style wrapper

React Payment Input's default style wrapper can be customized by supplying a styles prop.

import { css } from 'styled-components';
import { usePaymentInputs, PaymentInputsWrapper } from 'react-payment-inputs';

function PaymentForm() {
  const {
    getCardNumberProps,
    getExpiryDateProps,
    getCVCProps,
    wrapperProps
  } = usePaymentInputs();

  return (
    <PaymentInputsWrapper
      {...wrapperProps}
      styles={{
        fieldWrapper: {
          base: css`
            margin-bottom: 1rem;
          `
        },
        inputWrapper: {
          base: css`
            border-color: green;
          `,
          errored: css`
            border-color: maroon;
          `,
          focused: css`
            border-color: unset;
            box-shadow: unset;
            outline: 2px solid blue;
            outline-offset: 2px;
          `
        },
        input: {
          base: css`
            color: green;
          `,
          errored: css`
            color: maroon;
          `,
          cardNumber: css`
            width: 15rem;
          `,
          expiryDate: css`
            width: 10rem;
          `,
          cvc: css`
            width: 5rem;
          `
        },
        errorText: {
          base: css`
            color: maroon;
          `
        }
      }}
    >
      <input {...getCardNumberProps()} />
      <input {...getExpiryDateProps()} />
      <input {...getCVCProps()} />
    </PaymentInputsWrapper>
  );
}

See the example on Storybook

Custom card images

The card images can be customized by passing the images attribute to getCardImageProps({ images }). The images object must consist of SVG paths.

import { css } from 'styled-components';
import { usePaymentInputs, PaymentInputsWrapper } from 'react-payment-inputs';

const images = {
  mastercard: (
    <g fill="none" fillRule="evenodd">
      <rect fill="#252525" height="16" rx="2" width="24" />
      <circle cx="9" cy="8" fill="#eb001b" r="5" />
      <circle cx="15" cy="8" fill="#f79e1b" r="5" />
      <path
        d="m12 3.99963381c1.2144467.91220633 2 2.36454836 2 4.00036619s-.7855533 3.0881599-2 4.0003662c-1.2144467-.9122063-2-2.36454837-2-4.0003662s.7855533-3.08815986 2-4.00036619z"
        fill="#ff5f00"
      />
    </g>
  )
}

function PaymentForm() {
  const {
    getCardNumberProps,
    getExpiryDateProps,
    getCVCProps,
    getCardImageProps,
    wrapperProps
  } = usePaymentInputs();

  return (
    <PaymentInputsWrapper {...wrapperProps}>
      <svg {...getCardImageProps({ images })} />
      <input {...getCardNumberProps()} />
      <input {...getExpiryDateProps()} />
      <input {...getCVCProps()} />
    </PaymentInputsWrapper>
  );
}

License

MIT © Medipass Solutions Pty. Ltd.

react-payment-inputs's People

Contributors

asdolo avatar dependabot[bot] avatar doganalper avatar dsizomin avatar hopp3r avatar jxom avatar nordfjord avatar thepenskefile avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-payment-inputs's Issues

Warning: require cycle

Hello and thank you very much for the library. The experience so far is really good but I think it might be important to raise one issue. I have not faced any errors related to this warning so far but it would be great if you could fix it to avoid possible future issues. This warning shows up right after the build:

Require cycle: node_modules/react-native-payment-inputs/dist/hooks/index.js -> node_modules/react-native-payment-inputs/dist/hooks/usePaymentInputs.js -> node_modules/react-native-payment-inputs/dist/hooks/index.js

Require cycles are allowed, but can result in uninitialized values. Consider refactoring to remove the need for a cycle.

Node: 12.18.1
iOS: 14.3
Simulator: iPhone 11

QUESTION: Form Complete?

Is there any standard way to have one onComplete method for the entire form? Looking to replicate the same functionality found in the Stripe Elements API whereby I can listen onChange for the form and then check the form to see if its completed.

Avoid leaking sensitive data, remove "name" attribute from form fields

Hi! Thank you for this component 🙂

When using credit card forms, generally it's a good idea to avoid sending sensitive data to own servers (unless you are PCI/DSS compliant). Currently, the component adds a name attribute to all fields, making it possible to send credit card information to own servers by mistake.

IMHO, adding the name attribute should be opt-in only, for example like this:

getCardNumberProps({ withNameProp: true })

What do you think? This would be a breaking change, requiring a major version bump.

Custom card validation not working - cardNumberValidator

Hi team,

Need your help to enable and validate - UATP card payment type.

I tried to configure custom card validation through cardNumberValidator function.

This function not calling due to utils -> validator.js ->
getCardNumberError function -> validateLuhn function is blocked to call the cardNumberValidator function.

Because of this we can't able to add custom card validation.

Could you please help on this ASAP.

Can't use without styled-components

Even though I am using only usePaymentInputs, I can't seem to use it without needing to install styled-components.

Module not found: Can't resolve 'styled-components' in '/home/romaia/dev/stoq/contactless-payment/node_modules/react-payment-inputs/es'

Deleting the card number from middle is deleting in reverse

Enter 4242 4242 4242 4242 on the Card number field
Place the cursor after the eighth digit and start deleting the digits

Actual result

When placing the cursor on the middle of the credit card number (after the eighth digit) on the Card number field and starting to delete the number, the first four numbers are deleted, then the cursor stops, and the last digits (from the right of the cursor) begin to be deleted

Expected result

The user should be able to delete all the digits from the left of the cursor when repositioning the cursor to the middle of the number of the credit card (after the eighth digit) and after that, the cursor should stop at the beginning of the field while the remain digits should not be deleted

Allow empty CVC

You can change the CVC validation for invalid CVC but you cannot the require settings

Type error for CardImages typescript

Hello,

Trying to use PaymentInputsWrapper in a typescript app and get this error during build:

Type error: Type 'typeof import("/Users/main/.../node_modules/@types/react-payment-inputs/images/index")' is missing the following properties from type 'CardImages': amex, dinersclub, discover, hipercard, and 5 more.

Any ideas how to resolve this? It functions fine but wont build.

Can't use without adding styled-components

Even though I am using only usePaymentInputs, I can't seem to use it without needing to install styled-components.

Module not found: Can't resolve 'styled-components' in 'node_modules/react-payment-inputs/es'

isn't styled components only necessary if using PaymentInputsWrapper?

Errors not shown when tabbing to inputs outside this library

What's the rational for this check?

if (document.activeElement.tagName !== 'INPUT') {
setIsTouched(true);
} else if (value === false) {
setIsTouched(false);
}

Because the net effect is that validation errors aren't displayed when tabbing to other fields within the form that aren't part of this library. Is that intentional?

Since cardNumber isn't valid or invalid based on the validity of the other fields, why not show errors (if any) as soon as it looses focus?

Or, at a minimum to maintain a similar behavior, how about just checking to see if library fields are active?

if (
  document.activeElement !== cardNumberField.current &&
  document.activeElement !== expiryDateField.current &&
  document.activeElement !== cvcField.current &&
  document.activeElement !== zipField.current
) {
  setIsTouched(true);
} else if (value === false) {
  setIsTouched(false);
}

hasErrored warning

Warning: React does not recognize the `hasErrored` prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase `haserrored` instead. If you accidentally passed it from a parent component, remove it from the DOM element.

Using NextJS 13 and client-side rendering.

Validation fails and wrongly marks cardNumber as invalid if we set custom error messages

I'm following the example in https://github.com/medipass/react-payment-inputs#bootstrap

My code is exactly the same in the example:

const ERROR_MESSAGES = {
        emptyCardNumber: 'Insira o número de um cartão válido',
        invalidCardNumber: 'Número do cartão inserido é inválido',
        emptyExpiryDate: 'Data de vencimento inválida',
        monthOutOfRange: 'O mês de vencimento deve estar entre 01 e 12',
        yearOutOfRange: 'Ano de vencimento inválido',
        dateOutOfRange: 'Data de vencimento inválida',
        invalidExpiryDate: 'Data de vencimento inválida',
        emptyCVC: 'Insira um código CVV válido',
        invalidCVC: 'Código CVV inserido inválido'
    };

[...]
return (<Form>
        <Form.Control
            // Here is where React Payment Inputs injects itself into the input element.
            {...getCardNumberProps()}
            // You can retrieve error state by making use of the error & touched attributes in `meta`.
            isInvalid={touchedInputs.cardNumber && erroredInputs.cardNumber}
            placeholder="0000 0000 0000 0000"
          />
...

after typing my real credit card number, the validation runs and sets the input as invalid.

this is the meta returned:

code: {name: "CVC", length: 3}
displayName: "Mastercard"
format: /(\d{1,4})/g
gaps: (3) [4, 8, 12]
lengths: [16]
startPattern: /^(5[1-5]|677189)|^(222[1-9]|2[3-6]\d{2}|27[0-1]\d|2720)/
type: "mastercard"
__proto__: Object
error: "Número do cartão inserido é inválido"
erroredInputs: {cardNumber: "Número do cartão inserido é inválido", expiryDate: undefined, cvc: undefined, zip: undefined}
focused: undefined
isTouched: true
touchedInputs: {cardNumber: true, expiryDate: true, cvc: true, zip: false}
__proto__: Object

SSR shows an warning with useLayoutEffect

I'm using this package with Next.js and works awesome but this shows an warning in devmode because useLayoutEffect is used only for client-side applications for SSR application is better use useEffect according to documentation.

Any suggestion for solve this warning?

image

onError handler not firing

The onError handler doesn't seem to be firing.

<input {...getCardNumberProps({ onError: () => console.log('SOME ERROR') })} />

This never logs. Other handlers do work.

When is it supposed to fire? I would expect it to run if I type something invalid into the box.

Reset inputs after submit form

Any way to reset input values after submit?, right now the only way, at least form me, is by using ref.

const cardNum = useRef(null);
 
   onSubmit = {(values, actions) => {
        if( cardExp.current && cardNum.current && cusAmount.current ) {
            cardNum.current.value = '';
        }
   }}

This works, BUT, if you look the meta object, looks like no errors were setting, so erroredInputs propertie looks like this:

erroredInputs: {
   cardNumber: undefined
   cvc: undefined
   expiryDate: undefined
   zip: undefined
}

And this is a problem because there is no errors to be able of validate again.

By the way, I´m using formik and material UI.

Thanks for this package and if you could give me any advice on this one.

Best regards

Ant Design compatibility

How can I use this library with Ant Design?

Attempting a minimal working example as follows...

<Input {...getCardNumberProps()} />

...produces this error:

Uncaught TypeError: Cannot read property 'match' of undefined
    at Object.formatCardNumber (formatter-b0b2372d.js:6)
    at usePaymentInputs.js:518
    at commitHookEffectListMount (react-dom.development.js:20573)
    at commitLifeCycles (react-dom.development.js:20634)
    at commitLayoutEffects (react-dom.development.js:23426)
    at HTMLUnknownElement.callCallback (react-dom.development.js:3945)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:3994)
    at invokeGuardedCallback (react-dom.development.js:4056)
    at commitRootImpl (react-dom.development.js:23151)
    at unstable_runWithPriority (scheduler.development.js:468)
    at runWithPriority$1 (react-dom.development.js:11276)
    at commitRoot (react-dom.development.js:22990)
    at performSyncWorkOnRoot (react-dom.development.js:22329)
    at react-dom.development.js:11327
    at unstable_runWithPriority (scheduler.development.js:468)
    at runWithPriority$1 (react-dom.development.js:11276)
    at flushSyncCallbackQueueImpl (react-dom.development.js:11322)
    at flushSyncCallbackQueue (react-dom.development.js:11309)
    at discreteUpdates$1 (react-dom.development.js:22420)
    at discreteUpdates (react-dom.development.js:3756)
    at dispatchDiscreteEvent (react-dom.development.js:5889)

NPM 1.1.2

Hello,

We found out that the last version of the package (1.1.2) was not published on NPM...
This last one is fixing some issues inside usePaymentInputs that we need.

Please publish it 🙏

Thanks

Run validation again after paste / autofill / password manager fill

When pasting a Visa number which includes dashes, such as 4111-1111-1111-1111, the dashes are removed correctly and the input’s value is formatted and displayed with spaces instead, but the validation state doesn’t update; instead it reports the stale message Card number is invalid.

If I delete the last digit and input it again manually, the validation state updates and all is good – which makes for a subpar user experience.

Types file for Typescript

Anyone has created types/used it with typescript?
I am using it in the React Typescript and it's giving me the error of Types not found

Install: npm install react-payment-inputs --save
Error: Module not found: Can't resolve 'react-payment-inputs' in 'project_react/src/payment'

Could not find a declaration file for module 'react-payment-inputs'. 'project_react/node_modules/react-payment-inputs/lib/index.js' implicitly has an 'any' type.
Try npm install @types/react-payment-inputs if it exists or add a new declaration (.d.ts) file containing declare module 'react-payment-inputs';ts(7016)

CVV allows alphanumeric characters & no limits

As the title says. Alphanumeric characters can be input in the cvv field & there is no limit to the amount of characters. I might have missed something in the documentation to add those limitations, if so let me know and I'll close this issue!

Strange cursor behavior when changing value in card number and expiration date fields

First of all, thank you so much for this library! We're integrating it with Formik and it helped us a lot.

I was comparing behavior of this library and Stripe forms and noticed 2 differences, which Stripe handles (arguably) better.

The first issue is that when you have an expiration month that starts with 0 (e.g. 02) and you put your cursor right after 0, you can delete 0 and replace it with any character, not just 0. Stripe does not allow to delete 0 in such case - if you press backspace, it will just move the cursor before 0.

The second issue is that when you have a card number like 4242 4242 4242 4242 and you put the cursor right after the first 4 digit in the last group (4242 4242 4242 4|242) and you press backspace, it will keep the cursor in place and shift digits on the right of the cursor to the left, e.g. 4242 4242 4242 2|42, 4242 4242 4242 4|2, 4242 4242 4242 2|. Stripe will simply delete the digit and put your cursor at the end of the previous group , e.g. 4242 4242 4242| 242.

I can try to come up with a PR, but I'm not sure if/when I'll have time to do it :/

Override Formik Field Names

Issue
After going through the code, it doesn't appear that the ability to override the name of a Formik field is provided (see here). It appears to be hardcoded to specific values regardless of the name prop that usually controls that mapped value.

Proposal
Allow users to override the name of any Formik Field

Example:
<Field id="myCustomCardNumber" name="myCustomCardNumber"> {({ field }) => <input {...getCardNumberProps({ onBlur: field.onBlur, onChange: field.onChange})} />}</Field>

focustOut not display error message

Hi there,
Thanks so much for your package it's like reborn react-credit-card-input.
All validation and event work clearly right. But still, one issue (maybe only mine) left, here is description:

  1. Fill card inputs and required fields. Validation for button works properly.
    image

  2. An error message displays only if I again put a focus on any input card field (step 1) and click out of fields (step 2).
    image

Regards

Incorrect behavior of CVC validator if credit card type is not known

CVC validator doesn't work correctly, i.e. returns true for invalid (e.g. 1 digit only) CVC number, if there's no credit card type available yet. This may happen if someone tries to fill out CVC field, without filling out credit card number field yet, though I admit that it's probably very, very unlikely 😄. Combined with automatic focus of the next field, in this case ZIP code field, it automatically moves focus to this field after typing any digit.

I'm not sure if it handles all cases, but maybe it would be ok to assume that CVC is invalid, if it has less than 3 digits? Additionally, once credit card type is known, it could check if CVC should have 3 or 4 digits.

Remove "zero-dependency" from README

The README currently describes the project as a A zero-dependency React Hook & Container to help with payment card input fields. but then goes on to say the React Payment Inputs requires styled-components to be installed as a dependency.

This may seem small, but it's still confusing.

Expiry date don't format input data

I'm working with react-payment-inputs and material ui:

const { meta, getCardNumberProps, getExpiryDateProps, getCVCProps, getCardImageProps } = usePaymentInputs();
const { erroredInputs, touchedInputs } = meta;

...

const [expiryDate, setExpiryDate] = useState();
const handleChangeExpiryDate = (event) => setExpiryDate(event.target.value);

...

<TextField
  inputProps={{ "data-testid": "expireDate" }}
  {...getExpiryDateProps({ onChange: handleChangeExpiryDate })}
  defaultValue = { expiryDate }
  error={ (erroredInputs.expiryDate && touchedInputs.expiryDate) ? true : false } 
  helperText= { (touchedInputs.expiryDate) && erroredInputs.expiryDate }
  label="Expiry Date"
  type="text"
  margin="dense"
  fullWidth
/>                                

In this example validation work fine but don't format the input date as 00/00

Screenshot Capture - 2020-07-02 - 12-49-12

If I implement a simple input everything it's ok

<input {...getExpiryDateProps({ onChange: handleChangeExpiryDate })} value={expiryDate} />

Saludos, muchas gracias.

Suggestion to update README section about options for usePaymentInputs

This might be obvious for people who have more experience with React hooks, but code like this:

function MyComponent() {
  const paymentInputs = usePaymentInputs({
    errorMessages: {
      emptyCardNumber: {
        id: 'CheckoutPage.StepPayment.CreditCardForm.Error.emptyCardNumber',
      },
    },
  });

  ...

  return <Whatever />;
}

will cause an infinite render loop, because errorMessages is a nested object that is recreated on every render, so even though errorMessages is marked as a dependency in useEffect hook used internally by this library, it will use === comparison, always return false and cause re-render.

It can be easily fixed by defining error messages outside of the component:

const ERROR_MESSAGES = {
  emptyCardNumber: {
    id: 'CheckoutPage.StepPayment.CreditCardForm.Error.emptyCardNumber',
  }
};

function MyComponent() {
  const paymentInputs = usePaymentInputs({  errorMessages: ERROR_MESSAGES });

  ...

  return <Whatever />;
}

Maybe it could be mentioned in the README, or maybe there's some way to handle it by the library itself, or maybe everyone who is more familiar with React hooks knows about it already ;)

Export/Import react-payment-inputs/images (documentation error?)

Not sure if we're doing something wrong here and/or need to do an additional yarn install, but when trying to use the PaymentInputsWrapper, we are having an issue w/ basic import of the credit card images: import images from 'react-payment-inputs/images';.

It gives us an error of:

Cannot find module: 'react-payment-inputs/images'. Make sure this package is installed.

You can install this package by running: yarn add react-payment-inputs/images.

Looked inside of the installed node_modules directory and see the react-payment-inputs/es (and /lib and /umd) folders - with the /es and /lib ones each containing the subfolder for images but not sure how they are exported in there via rollup such that the images can be imported properly??

Any idea on what we're doing wrong?

Do we need to explicitly import the es6 version? (import images from 'react-payment-inputs/es/images';)

Vue

This is the most elegant input for cc information. Is there a vue version?

Card number - support setting the input type

Hi,
In order to support setting the input type, it only required to change the type prop of getCardNumberProps (ln 159):

type: props.type || 'tel',

and then it can be configured like this:

{...getCardNumberProps({ onChange: handleChangeCardNumber, type: "password" })}

Can you provide me a contributing permission please?
Thanks

Allow modifying placeholder text

Currently, the error message text can easily be specified, for translations, for example.

However, the placeholder text is hardcoded.

If it were possible to override the placeholder text in the same way that is currently possible for the error message text, then that would make it much easier to provide localisation.

Focus automatically jumps to expiryDateField

When we have an order of inputs like this:
image
and we input the valid card number, focus is automatically switched to the expiryDateField, missing the custom name field.
This happens because of this line:

expiryDateField.current && expiryDateField.current.focus();

Expected behaviour:
automatic focusing can either be disabled, or we can specify the ref for the next-focus-element ourselves.

Different format for ExpiryDate?

Currrently the default is "MM / YY". I need it to be "MM/YYYY". I can format using string methods after i get the data, but it would be nice if i could get directly from the input

meta.isTouched should omit zip field when not provided

Expected Behavior:

If no zip input is provided, it should be omitted from meta.touched and meta.isTouched.

Actual Behavior:

I setup react-payment-inputs as follows:

 const {
    getCardNumberProps,
    getExpiryDateProps,
    getCVCProps,
    wrapperProps, 
    getCardImageProps, 
    meta
  } = usePaymentInputs();

return (
  <>
     <PaymentInputsWrapper {...wrapperProps} styles={style}>
        <svg {...getCardImageProps({ images })} />
        <input {...getCardNumberProps()} />
        <input {...getExpiryDateProps()} />
        <input {...getCVCProps()} />
      </PaymentInputsWrapper>
      { meta.isTouched && meta.error && <span>Error: {meta.error}</span> } 
  </>
); 

For the life of me, I couldn't understand why the error was not rendering. However, upon further inspection I found the following:

meta.isTouched
==> {cardNumber: true, expiryDate: true, cvc: true, zip: false}

Since I'm not providing a zip field, it's never touched and meta.touched is always false, making it impossible to render the error message. It seems likely that I could be doing something wrong, but I couldn't find a solution to this in the docs or by searching through closed issues.

Formatting not working after the value prop is added

Just as described in the redme, placed the event handler inside the getter props but formatting is not applied.

import React, { useState } from "react";
import { usePaymentInputs } from "react-payment-inputs";

export default function PaymentInputs() {
  const [number, setNumber] = useState("");
  const [expiry, setExpiry] = useState("");
  const [cvc, setCvc] = useState("");

  const {
    meta,
    getCardNumberProps,
    getExpiryDateProps,
    getCVCProps
  } = usePaymentInputs();

  return (
    <div>
      <div>
        <input
          {...getCardNumberProps({
            onChange: e => setNumber(e.target.value)
          })}
          value={number}
        />
      </div>
      <div>
        <input
          {...getExpiryDateProps({
            onChange: e => setExpiry(e.target.value)
          })}
          value={expiry}
        />
      </div>
      <div>
        <input
          {...getCVCProps({ onChange: e => setCvc(e.target.value) })}
          value={cvc}
        />
      </div>
      {meta.isTouched && meta.error && <div>Error: {meta.error}</div>}
      <button>place order</button>
    </div>
  );
}

what am i doing wrong?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.