Skip to main content

Field Components

@accrupay/react exports five field components: CardholderName, CardNumber, CardExpiry, CardCVC, and SubmitButton. All must be rendered inside an <AccruPay> provider.

CardholderName

Renders a provider-appropriate text input for the name on the card. For providers that require SDK-managed name input, the component renders the provider's own element; otherwise it renders a standard HTML <input>.

Accepts all standard React.InputHTMLAttributes<HTMLInputElement> except value (the field is uncontrolled internally).

PropTypeNotes
classNamestringApplied to the input element.
styleReact.CSSPropertiesInline styles.
placeholderstringPlaceholder text.
onChangeReact.ChangeEventHandler<HTMLInputElement>Standard change handler.
onBlurReact.FocusEventHandler<HTMLInputElement>Standard blur handler.
disabledbooleanDisables the input.
autoCompletestringRecommend "cc-name".
Any other input attrPassed through to the underlying element.
<CardholderName
placeholder="Name on card"
autoComplete="cc-name"
className="input"
onChange={(e) => console.log(e.target.value)}
/>

CardNumber, CardExpiry, CardCVC

warning

These components render provider-hosted embedded fields — secure iframes or SDK-injected inputs from the payment provider. They do not accept input event handlers (onChange, onBlur, onFocus, value, etc.). Adding those props has no effect.

Use isReady from useAccruPay to know when these fields are interactive and ready to accept input.

Each component accepts only layout props:

PropTypeDescription
classNamestringCSS class applied to the wrapper element.
styleReact.CSSPropertiesInline styles on the wrapper element.
<CardNumber className="card-field" style={{ marginBottom: 12 }} />
<CardExpiry className="card-field" />
<CardCVC className="card-field" />

Style the wrapper via className and style. The embedded field inside fills the wrapper. Set a fixed height (typically 40–56 px) to ensure the field is visible — embedded fields do not inherit height from content flow.

SubmitButton

A button that calls submitPayment() on click. Extends React.ButtonHTMLAttributes<HTMLButtonElement> with additional callbacks.

PropTypeRequiredDescription
childrenReactNodeyesButton label.
onSuccess(result: any) => voidnoCalled with the provider result on successful payment.
onError(error: Error) => voidnoCalled with the thrown error on failure.
submitParamsAccruPayTransactionSubmitParamsnoBilling override passed to submitPayment().
disabledbooleannoDisables the button. Combined with internal isProcessing check.
Any other button attrPassed through to the underlying <button>.
<SubmitButton
className="pay-button"
disabled={!isReady}
onSuccess={(result) => router.push('/order/confirmed')}
onError={(err) => console.error(err)}
>
Pay now
</SubmitButton>

SubmitButton is disabled automatically while isProcessing is true, regardless of the disabled prop.

For custom submission logic (async billing lookup, form validation before submit), call submitPayment() from useAccruPay directly rather than using SubmitButton.

Styling

None of the field components ship built-in styles. Wrap them in your own layout elements.

<div className="card-form">
<label>Name on card</label>
<CardholderName className="input" placeholder="Jane Smith" autoComplete="cc-name" />

<label>Card number</label>
<CardNumber className="input embedded" style={{ height: 48 }} />

<div className="card-form__row">
<div>
<label>Expiry</label>
<CardExpiry className="input embedded" style={{ height: 48 }} />
</div>
<div>
<label>CVC</label>
<CardCVC className="input embedded" style={{ height: 48 }} />
</div>
</div>
</div>

Complete form example

import {
AccruPay,
CardholderName,
CardNumber,
CardExpiry,
CardCVC,
SubmitButton,
useAccruPay,
} from '@accrupay/react';

function CheckoutForm() {
const { isReady, error } = useAccruPay();

return (
<div className="form">
<div className="field">
<label htmlFor="name">Name on card</label>
<CardholderName
id="name"
className="input"
placeholder="Jane Smith"
autoComplete="cc-name"
/>
</div>

<div className="field">
<label>Card number</label>
<CardNumber className="input" style={{ height: 48 }} />
</div>

<div className="field-row">
<div className="field">
<label>Expiry</label>
<CardExpiry className="input" style={{ height: 48 }} />
</div>
<div className="field">
<label>CVC</label>
<CardCVC className="input" style={{ height: 48 }} />
</div>
</div>

{error && <p className="error">{error}</p>}

<SubmitButton
className="btn-primary"
disabled={!isReady}
onSuccess={() => window.location.href = '/confirmed'}
onError={(err) => console.error(err)}
>
Pay now
</SubmitButton>
</div>
);
}

export function Checkout({ sessionId }: { sessionId: string }) {
return (
<AccruPay
merchantPublicId={process.env.NEXT_PUBLIC_ACCRUPAY_MERCHANT_ID!}
transactionSessionId={sessionId}
>
<CheckoutForm />
</AccruPay>
);
}