@paygood1/collect-react
v0.15.0
Published
React wrapper for PayGood card collection core
Readme
@paygood1/collect-react
React wrapper for @paygood1/collect-core with a drop-in
<PaymentInstrumentCollect /> component.
Install
npm install @paygood1/collect-react @paygood1/collect-corePayment instruments
Set the instrument prop to choose what to collect:
| instrument | Description |
| --- | --- |
| "card" (default) | Card number, expiration, and CVC |
| "bank_account" | US ACH or Canadian EFT bank account details |
For bank accounts, use allowedBankCountries to restrict collection to "US",
"CA", or both. When a single country is provided, the in-component country
selector is hidden and that country is used automatically.
Basic usage
Your backend must bootstrap a session key before rendering the component. First create a tokenization intent, then exchange that intent token for a session key:
POST /tokenization/intentswith your merchant API key as bearer auth and acustomerIdin the request body.- Use the
tokenvalue returned from step 1 as bearer auth onPOST /tokenization/sessions.
Pass the resulting sessionKey into the component:
import { PaymentInstrumentCollect } from "@paygood1/collect-react";
const sessionKey = await fetch("/api/tokenization/session", {
method: "POST"
}).then((response) => response.json()).then((body) => body.sessionKey);
export function Checkout() {
return (
<PaymentInstrumentCollect
instrument="card"
mode="sandbox"
sessionKey={sessionKey}
onTokenized={(result) => {
if (result.kind === "token_intent") {
chargeWithIntent(result.tokenIntentId);
} else {
chargeWithToken(result.tokenId);
}
}}
/>
);
}Bank account example with country restrictions:
<PaymentInstrumentCollect
instrument="bank_account"
allowedBankCountries={["US"]}
mode="sandbox"
sessionKey={sessionKey}
onTokenized={(result) => {
if (result.kind === "token_intent") {
chargeWithIntent(result.tokenIntentId);
} else {
chargeWithToken(result.tokenId);
}
}}
/>Perform the intent and session requests server-side. Do not expose your merchant API key in the browser.
Save card behavior
By default the component shows a Save for later checkbox (unchecked). One-off checkouts create a token intent; checking the box creates a persistent token.
For subscription or mandate flows where the card must be saved, pass
requireSaveCard. This hides the checkbox and always creates a token:
<PaymentInstrumentCollect
instrument="card"
mode="sandbox"
sessionKey={sessionKey}
requireSaveCard
tokenizeButtonLabel="Subscribe"
onTokenized={(result) => {
console.log(result.kind === "token" ? result.tokenId : result.tokenIntentId);
}}
/>Tokenization result
onTokenized receives a discriminated TokenizationResult:
type TokenizationResult =
| { kind: "token"; tokenId: string; fingerprint: string; type: "card" | "bank_account" }
| {
kind: "token_intent";
tokenIntentId: string;
fingerprint: string;
type: "card" | "bank_account";
};When the save checkbox is unchecked (default), the result is a token intent.
When checked, or when requireSaveCard is set, the result is a persistent token.
Session key from your backend
Use a backend route to run the intent-to-session flow and return only
sessionKey to the browser:
// Server-side example
const intentResponse = await fetch("https://api-sandbox.paygood.co/tokenization/intents", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_MERCHANT_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({ customerId: "cust_123" })
});
const { token } = await intentResponse.json();
const sessionResponse = await fetch("https://api-sandbox.paygood.co/tokenization/sessions", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json"
}
});
const { sessionKey } = await sessionResponse.json();Then pass it to the component:
<PaymentInstrumentCollect
mode="sandbox"
sessionKey={sessionKey}
onTokenized={(result) => {
if (result.kind === "token_intent") {
chargeWithIntent(result.tokenIntentId);
} else {
chargeWithToken(result.tokenId);
}
}}
/>Props
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| instrument | "card" \| "bank_account" | "card" | Payment instrument to collect |
| allowedBankCountries | AllowedBankCountry[] | all countries | Bank flows only. Restrict to "US", "CA", or both |
| mode | "sandbox" \| "production" | "sandbox" | PayGood environment |
| sessionKey | string | — | Basis Theory session key from your backend |
| mountOptions | object | — | Passed to core.mount() |
| requireSaveCard | boolean | false | Subscription/mandate flows. Hides checkbox and always creates a token |
| saveCardLabel | string | "Save for later" | Checkbox label when requireSaveCard is false |
| tokenizeButtonLabel | string | "Confirm" | Submit button label |
| collectCustomerDetails | boolean | true | Collect full name and billing address on card flows only (ignored for bank) |
| defaultBillingAddressExpanded | boolean | false | Whether billing address starts expanded |
| customerDetails | CustomerDetails | — | Controlled customer details |
| defaultCustomerDetails | Partial<CustomerDetails> | — | Initial values in uncontrolled mode |
| onCustomerDetailsChange | (details) => void | — | Called whenever customer details change |
| onTokenized | (result, context?) => void | — | Called after tokenization; context.customerDetails is a submit-time snapshot |
| onChange | (payload) => void | — | Field changes and bank country changes |
| onReady | () => void | — | Called when the component is ready |
| onError | (error) => void | — | Called on errors |
Import AllowedBankCountry and ALLOWED_BANK_COUNTRIES from
@paygood1/collect-core when you need the canonical allowed values.
Customer details and billing address
By default the component collects a full name and a collapsible US/CA billing address before card tokenization. Bank account flows do not collect customer or billing details.
import {
PaymentInstrumentCollect,
type CustomerDetails
} from "@paygood1/collect-react";
export function Checkout() {
const [customerDetails, setCustomerDetails] = useState<CustomerDetails | null>(null);
return (
<PaymentInstrumentCollect
sessionKey={sessionKey}
onCustomerDetailsChange={setCustomerDetails}
onTokenized={(result, context) => {
createPaymentInstrument({
fullName: context!.customerDetails.fullName,
billingAddress: context!.customerDetails.billingAddress,
tokenIntentId: result.kind === "token_intent" ? result.tokenIntentId : undefined,
tokenId: result.kind === "token" ? result.tokenId : undefined
});
}}
/>
);
}Pass collectCustomerDetails={false} to disable customer fields and preserve the previous payment-only behavior.
