Định nghĩa kiểu
Mọi kiểu public của Bahn Web SDK, đúng như SDK khai báo. Các trang Tham chiếu SDK và Luồng thanh toán đều liên kết đến đây để xem cấu trúc đầy đủ của một tham số hoặc kết quả.
Dùng các kiểu trong dự án của bạn
SDK được tải dưới dạng một <script> thông thường, nên không có package nào để cài kiểu. Thay vào đó, hãy tải file khai báo:
Lưu file vào dự án (ví dụ types/bahn.d.ts) và đảm bảo include trong tsconfig.json bao gồm file này. File khai báo biến toàn cục Bahn, window.BahnCake và mọi kiểu bên dưới trong namespace Bahn — không cần import:
const bahn = new Bahn({ pk: "pk_test_...", environment: "sandbox" });
const options: Bahn.CardMountOptions = {
layout: { columns: 1 },
allowedSchemes: ["visa", "mastercard"],
};
const card = bahn.mountCard("#card-container", options);
const { payment_intent, three_ds_payload } = await bahn.confirmPayment({ client_secret });
// ^ Record<string, unknown> ^ Bahn.ThreeDSPayloadDecoded | undefinedCác đoạn mã bên dưới bỏ tiền tố Bahn. vì chúng được viết bên trong namespace. Chú thích trong mã giữ nguyên tiếng Anh, giống hệt file bahn.d.ts.
Số tiền
Mọi amount đều tính theo đơn vị tiền tệ nhỏ nhất — VND giữ nguyên, USD tính theo cent.
Lớp Bahn
Biến toàn cục mà script định nghĩa. Là singleton — gọi new Bahn() lần thứ hai sẽ trả về instance hiện có. Xem Tham chiếu SDK để biết từng phương thức làm gì.
/** The Bahn SDK. A singleton — a second `new Bahn()` returns the existing instance. */
declare class Bahn {
constructor(options: Bahn.BahnOptions);
getConfig(): Promise<Bahn.BahnConfig>;
mountCard(containerId: string, options?: Bahn.CardMountOptions): Bahn.CardComponent;
mountPaymentQR(selector: string, clientSecret: string, options?: Bahn.QRMountOptions): Bahn.IQRComponent;
isApplePayAvailable(): Promise<boolean>;
/** Pass `clientSecret` for direct confirmation, `undefined` for the token-based flow. */
mountApplePay(
selector: string,
clientSecret: string | undefined,
options: Bahn.ApplePayButtonOptions,
): Bahn.IApplePayButtonComponent;
unmountApplePay(): void;
readonly applePay: Bahn.IApplePay;
isGooglePayAvailable(): Promise<boolean>;
/** Pass `clientSecret` for direct confirmation, `undefined` for the token-based flow. */
mountGooglePay(
selector: string,
clientSecret: string | undefined,
options: Bahn.GooglePayButtonOptions,
): Bahn.IGooglePayButtonComponent;
unmountGooglePay(): void;
mount3DS(
sessionId: string,
callbacks: Bahn.ThreeDSCallbacks,
options?: Bahn.ThreeDSecureOptions,
): Bahn.IThreeDSecure;
getChallengeRedirectUrl(sessionId: string, returnUrl: string, options?: { brandName?: string }): string;
/** Requires `mountCard()`. Rejects with `Bahn.APIError`. */
confirmPayment(payload: Bahn.ConfirmPaymentPayload): Promise<{
payment_intent: Record<string, unknown>;
/** Present only when 3DS authentication is required. */
three_ds_payload?: Bahn.ThreeDSPayloadDecoded;
}>;
/** Requires `mountCard()`. Rejects with `Bahn.APIError`. */
confirmCardSetup(payload: Bahn.ConfirmCardSetupPayload): Promise<Bahn.ConfirmCardSetupResult>;
/** Requires `mountCard()`. Rejects with `Bahn.APIError`. */
createConfirmationToken(payload: Bahn.CreateConfirmationTokenOptions): Promise<Bahn.ConfirmationTokenResult>;
handleNextAction(input: Bahn.PaymentIntentInput, callbacks: Bahn.HandleNextActionCallbacks): Promise<void>;
pollPaymentIntentStatus(clientSecret: string, callbacks: Bahn.PaymentIntentPollCallbacks): void;
stopPaymentIntentPolling(): void;
pollSetupIntentStatus(clientSecret: string, callbacks: Bahn.PaymentIntentPollCallbacks): void;
stopSetupIntentPolling(): void;
cleanup(): void;
}
interface Window {
Bahn: typeof Bahn;
BahnCake?: Bahn;
}Tùy chọn & cấu hình
Truyền vào new Bahn() và được trả về bởi bahn.getConfig(). Xem Tham chiếu SDK → BahnOptions.
SDKEnvironment
type SDKEnvironment = "sandbox" | "production";BahnOptions
interface BahnOptions {
/** Publishable key, sent as the `X-Bn-Publishable-Key` header. */
pk: string;
/** Target environment — selects the API and iframe domains. */
environment: SDKEnvironment;
/** Tenant app ID for 3DS routing. */
appId?: string;
/** Tenant team ID for 3DS routing. */
teamId?: string;
/** Maximum payment/setup intent polling attempts (default: 10). */
paymentIntentPollAttempts?: number;
}ApplePaySDKConfig
interface ApplePaySDKConfig {
enabled: boolean;
merchant_name: string;
supported_networks: string[];
merchant_capabilities: string[];
}BahnConfig
interface BahnConfig {
apple_pay: ApplePaySDKConfig;
}Thông tin thanh toán & confirmation token
Dùng bởi createConfirmationToken() và trường payment_method.billing_details của mọi lệnh xác nhận. Xem Luồng dựa trên token.
ConfirmationTokenAddress
interface ConfirmationTokenAddress {
country?: string;
city?: string;
line1?: string;
line2?: string;
postal_code?: string;
}ConfirmationTokenBillingDetails
interface ConfirmationTokenBillingDetails {
name?: string;
email?: string;
phone?: string;
address?: ConfirmationTokenAddress;
}CreateConfirmationTokenOptions
interface CreateConfirmationTokenOptions {
payment_method?: {
billing_details?: ConfirmationTokenBillingDetails;
};
[key: string]: unknown;
}ConfirmationTokenResult
interface ConfirmationTokenResult {
confirmation_token: string;
}Xác nhận intent
Payload, kết quả và callback của confirmPayment(), confirmCardSetup(), handleNextAction() và các phương thức polling. SDK khai báo đối tượng payment_intent và setup_intent là Record<string, unknown> — các trường của chúng được mô tả trong Tham chiếu API. Xem Xác nhận thanh toán.
ThreeDSPayloadDecoded
/** Decoded 3DS payload returned when an intent needs 3DS authentication. */
interface ThreeDSPayloadDecoded {
/** 3DS provider identifier. */
provider: string;
/** Tenant app ID. */
app_id: string;
/** Tenant team ID. */
team_id: string;
/** 3DS session ID — pass to `mount3DS()` or `getChallengeRedirectUrl()`. */
id: string;
}ConfirmPaymentPayload
interface ConfirmPaymentPayload {
/** Client secret of the payment intent created by your backend. */
client_secret: string;
/** Card details are added by the iframe — never pass them here. */
payment_method?: {
billing_details?: ConfirmationTokenBillingDetails;
};
/** Payment-method-specific options. */
payment_method_options?: Record<string, unknown>;
/** Token from `createConfirmationToken()`. */
confirmation_token?: string;
/** Optional amount override, in the smallest currency unit. */
amount?: string;
}ConfirmCardSetupPayload
interface ConfirmCardSetupPayload {
/** Client secret of the setup intent created by your backend. */
client_secret: string;
/** Card details are added by the iframe — never pass them here. */
payment_method?: {
billing_details?: ConfirmationTokenBillingDetails;
};
/** Payment-method-specific options. */
payment_method_options?: Record<string, unknown>;
}ConfirmCardSetupResult
interface ConfirmCardSetupResult {
setup_intent: Record<string, unknown>;
/** Present only when 3DS authentication is required. */
three_ds_payload?: ThreeDSPayloadDecoded;
}PaymentIntentInput
/**
* Input to `handleNextAction()`: a payment intent `client_secret`, or a
* `payment_intent` object your backend already returned (must include
* `client_secret`).
*/
type PaymentIntentInput = string | (Record<string, unknown> & { client_secret: string });HandleNextActionCallbacks
interface HandleNextActionCallbacks {
onReady?: () => void;
onSuccess?: (paymentIntent: Record<string, unknown>) => void;
/** Receives the failed payment intent, or the 3DS error if the challenge failed. */
onFailure?: (errorOrIntent?: ComponentError | Record<string, unknown>) => void;
onTimeout?: () => void;
onError?: (error: Error) => void;
onProgress?: (attempt: number, maxAttempts: number, status: string) => void;
}PaymentIntentPollCallbacks
/** Shared by payment- and setup-intent polling. */
interface PaymentIntentPollCallbacks {
onSuccess?: (intent: Record<string, unknown>) => void;
onFailure?: (intent: Record<string, unknown>) => void;
onTimeout?: () => void;
onError?: (error: Error) => void;
onProgress?: (attempt: number, maxAttempts: number, status: string) => void;
}3DS
Dùng bởi bahn.mount3DS(). ThreeDSErrorCode liệt kê mọi giá trị mà ComponentError.code có thể nhận. Xem Xác thực 3DS.
ComponentError
interface ComponentError {
/** One of `ThreeDSErrorCode`. */
code: string;
message: string;
}ThreeDSErrorCode
type ThreeDSErrorCode =
| "boot-failed"
| "frame-load-error"
| "session-complete"
| "session-not-found"
| "session-unknown-error"
| "challenge-failed"
| "challenge-cancelled"
| "challenge-aborted"
| "something-went-wrong";ThreeDSCallbacks
interface ThreeDSCallbacks {
onReady?: () => void;
onSuccess: () => void;
onFailure: (error?: ComponentError | Record<string, unknown>) => void;
}ColorScheme
type ColorScheme = "normal" | "light" | "dark" | "only light" | "only dark" | "light dark";ThreeDSecureOptions
interface ThreeDSecureOptions {
/** Color scheme passed to the challenge iframe. */
colorScheme?: ColorScheme;
/** Explicit iframe size (default: full-viewport overlay). */
size?: { width: string; height: string };
/**
* Behavior when the issuer requires a challenge:
* `false` (default) shows it, `true` fails the session without UI,
* a function decides at challenge time.
*/
failOnChallenge?: boolean | (() => boolean | Promise<boolean>);
}ThreeDSecureEvents
interface ThreeDSecureEvents {
ready: () => void;
success: () => void;
failure: (error: ComponentError) => void;
error: (error: ComponentError) => void;
}IThreeDSecure
interface IThreeDSecure {
mount(selector?: string | HTMLElement): this;
unmount(): this;
update(options?: ThreeDSecureOptions): this;
/** Returns an unsubscribe function. */
on<T extends keyof ThreeDSecureEvents>(event: T, callback: ThreeDSecureEvents[T]): () => void;
}Thẻ
Dùng bởi bahn.mountCard(). Xem Thanh toán thẻ.
CardEventHandler
type CardEventHandler = (data?: unknown) => void;CardComponent
interface CardComponent {
mount(selector: string): void;
unmount(): void;
on(event: string, handler: CardEventHandler): void;
validate(): void;
/** Re-apply labels/placeholders without remounting (e.g. on language switch). */
applyConfig(fields: NonNullable<CardMountOptions["fields"]>, inputStyle?: CardInputStyle): void;
/** Re-apply base/focus/invalid styles without remounting. */
applyStyles(stateStyles: CardStateStyles): void;
/** Validity state only — card values never leave the iframes. */
readonly values: {
isComplete: boolean;
isValid: boolean;
errors?: Record<string, string>;
};
}CardInputStyle
/** Allowlisted input styles — any other property is dropped. */
interface CardInputStyle {
color?: string;
backgroundColor?: string;
fontSize?: string;
fontFamily?: string;
fontWeight?: string;
fontStyle?: string;
lineHeight?: string;
letterSpacing?: string;
textAlign?: string;
borderRadius?: string;
border?: string;
borderColor?: string;
borderWidth?: string;
padding?: string;
paddingTop?: string;
paddingBottom?: string;
paddingLeft?: string;
paddingRight?: string;
height?: string;
opacity?: string;
boxShadow?: string;
caretColor?: string;
}CardStateStyles
interface CardStateStyles {
/** Reasserted on blur. */
base?: CardInputStyle;
/** Applied while the field has focus. */
focus?: CardInputStyle;
/** Applied on blur when the field fails validation. */
invalid?: CardInputStyle;
}CardLayoutConfig
interface CardLayoutConfig {
/** 2 (default): number on row 1, expiry | cvc on row 2. 1: one field per row. */
columns?: 1 | 2;
/** Gap between all fields (default: "8px"). */
gap?: string;
columnGap?: string;
rowGap?: string;
/** Height of the card-number iframe (defaults to `fieldHeight`). */
cardNumberHeight?: string;
/** Height of the expiry/cvc iframes (default: "92px"). */
fieldHeight?: string;
}CardScheme
type CardScheme = "visa" | "mastercard" | "amex" | "jcb" | "discover" | "diners" | "unionpay";CardLogoScheme
/** Schemes with built-in logo artwork for `customLogos`. */
type CardLogoScheme = "visa" | "mastercard" | "jcb";CardFieldConfig
interface CardFieldConfig {
label?: string;
placeholder?: string;
/**
* number: keyed by `incomplete_number` | `invalid_number`.
* expiry: keyed by `incomplete_expiry` | `invalid_expiry_month` | `invalid_expiry_year`.
* cvc: a plain string.
*/
errorMsg?: Record<string, string> | string;
/** Help (?) tooltip — `cvc` only. */
tooltip?: boolean;
/** Hide the brand logos inside the input — `number` only. */
hideLogo?: boolean;
}CardNumberConfig
interface CardNumberConfig extends CardFieldConfig {
/** Fixed set of logos, shown regardless of the detected scheme. */
customLogos?: Array<CardLogoScheme | CustomLogoSvg>;
}CardMountOptions
interface CardMountOptions {
fields?: {
number?: CardNumberConfig;
expiry?: CardFieldConfig;
cvc?: CardFieldConfig;
};
layout?: CardLayoutConfig;
inputStyle?: CardInputStyle;
stateStyles?: CardStateStyles;
/** Reveal each CVV digit for ~500ms before masking (default: false). */
cvvPeek?: boolean;
/** Treat card numbers from other schemes as invalid. */
allowedSchemes?: CardScheme[];
}RectProps
interface RectProps {
type: "rect";
x: string;
y: string;
width: string;
height: string;
rx?: string;
fill?: string;
stroke?: string;
}PathProps
interface PathProps {
type: "path";
d: string;
fill?: string;
fillRule?: "nonzero" | "evenodd";
clipRule?: "nonzero" | "evenodd";
}GProps
interface GProps {
type: "g";
/** Local fragment references (`url(#id)`) only. */
clipPath?: string;
children: SVGChildProps[];
}SVGChildProps
type SVGChildProps = RectProps | PathProps | GProps;CustomLogoSvg
interface CustomLogoSvg {
width: string;
height: string;
fill?: string;
viewBox: string;
children: SVGChildProps[];
}QR
Dùng bởi bahn.mountPaymentQR(). Xem Thanh toán QR.
QRStyle
interface QRStyle {
dotsOptions?: {
color?: string;
type?: "square" | "dots" | "rounded" | "extra-rounded" | "classy" | "classy-rounded";
};
backgroundOptions?: {
color?: string;
};
cornersSquareOptions?: {
color?: string;
type?: "dot" | "square" | "extra-rounded";
};
}QRMountOptions
interface QRMountOptions {
/** Iframe height (default: "300px"). */
height?: string;
/** Polling interval in ms (default: 3000, minimum: 1000). Negative disables polling. */
pollIntervalMs?: number;
qrStyle?: QRStyle;
}QREventHandler
type QREventHandler = (data?: unknown) => void;IQRComponent
interface IQRComponent {
unmount(): void;
on(event: "ready" | "payment:success" | "payment:failed" | "payment:error", handler: QREventHandler): void;
off(event: "ready" | "payment:success" | "payment:failed" | "payment:error", handler: QREventHandler): void;
download(name?: string): void;
}Apple Pay
Dùng bởi bahn.mountApplePay() và bahn.applePay. Xem Apple Pay.
ApplePayCardNetwork
type ApplePayCardNetwork = "amex" | "discover" | "interac" | "jcb" | "masterCard" | "privateLabel" | "visa";ApplePayMerchantCapability
type ApplePayMerchantCapability = "supports3DS" | "supportsCredit" | "supportsDebit";ApplePayButtonType
type ApplePayButtonType =
| "add-money"
| "book"
| "buy"
| "check-out"
| "continue"
| "contribute"
| "donate"
| "order"
| "pay"
| "plain"
| "reload"
| "rent"
| "set-up"
| "subscribe"
| "support"
| "tip"
| "top-up";ApplePayButtonStyle
type ApplePayButtonStyle = "black" | "white" | "white-outline";ApplePayShippingMethod
interface ApplePayShippingMethod {
/** Echoed back in `"shippingmethodselected"`. */
identifier: string;
label: string;
detail?: string;
/** Smallest currency unit (0 for free shipping). */
amount: number;
}ApplePayShippingContact
/** Coarse address — no street-level fields until the payment is authorized. */
interface ApplePayShippingContact {
city?: string;
state?: string;
postalCode?: string;
country?: string;
countryCode?: string;
}ApplePayShippingContactUpdate
interface ApplePayShippingContactUpdate {
shippingMethods?: ApplePayShippingMethod[];
lineItems?: Array<{ label: string; amount: number }>;
amount?: number;
}ApplePayShippingMethodUpdate
interface ApplePayShippingMethodUpdate {
lineItems?: Array<{ label: string; amount: number }>;
amount?: number;
}ApplePayShippingContactSelectedEvent
interface ApplePayShippingContactSelectedEvent {
contact: ApplePayShippingContact;
resolve: (update: ApplePayShippingContactUpdate) => void;
reject: () => void;
}ApplePayShippingMethodSelectedEvent
interface ApplePayShippingMethodSelectedEvent {
shippingMethod: ApplePayShippingMethod;
resolve: (update: ApplePayShippingMethodUpdate) => void;
reject: () => void;
}ApplePayButtonOptions
interface ApplePayButtonOptions {
/** Smallest currency unit. */
amount: number;
/** ISO 4217, e.g. "VND". */
currency: string;
/** ISO 3166-1 alpha-2, e.g. "VN". */
country: string;
priceLabel?: string;
lineItems?: Array<{ label: string; amount: number }>;
/** Defaults to your merchant's configured value. */
merchantName?: string;
/** Default: "plain". */
type?: ApplePayButtonType;
/** Default: "black". */
style?: ApplePayButtonStyle;
/** Default: "en". */
locale?: string;
/** Pixels (default: 4). */
borderRadius?: number;
/** Default: 100% width, 48px height. */
size?: { width?: string | number; height?: string | number };
/** Defaults to your merchant's configured value. */
supportedNetworks?: ApplePayCardNetwork[];
/** Defaults to your merchant's configured value. */
merchantCapabilities?: ApplePayMerchantCapability[];
requestBillingAddress?: boolean;
requestPayerDetails?: ("name" | "email" | "phone")[];
/** Requires a `"shippingcontactselected"` listener. */
requestShipping?: boolean;
shippingMethods?: ApplePayShippingMethod[];
}IApplePayButtonComponent
interface IApplePayButtonComponent {
unmount(): void;
/** Returns an unsubscribe function. */
on(
event: "ready" | "success" | "token" | "error" | "cancel" | "shippingcontactselected" | "shippingmethodselected",
handler: (data?: unknown) => void,
): () => void;
}ApplePayValidateMerchantResult
interface ApplePayValidateMerchantResult {
/** Pass to `session.completeMerchantValidation()`. */
merchant_session: Record<string, unknown>;
}ApplePayConfirmPaymentPayload
interface ApplePayConfirmPaymentPayload {
client_secret: string;
/** `event.payment.token` from `onpaymentauthorized`, as-is. */
token: Record<string, unknown>;
billing_details?: ConfirmationTokenBillingDetails;
}ApplePayConfirmPaymentResult
interface ApplePayConfirmPaymentResult {
payment_intent: Record<string, unknown>;
three_ds_payload?: ThreeDSPayloadDecoded;
}ApplePayCreateConfirmationTokenPayload
interface ApplePayCreateConfirmationTokenPayload {
/** `event.payment.token` from `onpaymentauthorized`, as-is. */
token: Record<string, unknown>;
billing_details?: ConfirmationTokenBillingDetails;
}IApplePay
/** `bahn.applePay` — backend calls for a hand-rolled `ApplePaySession`. */
interface IApplePay {
validateMerchant(validationURL: string): Promise<ApplePayValidateMerchantResult>;
confirmPayment(payload: ApplePayConfirmPaymentPayload): Promise<ApplePayConfirmPaymentResult>;
createConfirmationToken(payload: ApplePayCreateConfirmationTokenPayload): Promise<ConfirmationTokenResult>;
}Google Pay
Dùng bởi bahn.mountGooglePay(). Xem Google Pay.
GooglePayCardNetwork
type GooglePayCardNetwork = "AMEX" | "DISCOVER" | "INTERAC" | "JCB" | "MASTERCARD" | "VISA";GooglePayAuthMethod
type GooglePayAuthMethod = "PAN_ONLY" | "CRYPTOGRAM_3DS";GooglePayButtonType
type GooglePayButtonType = "book" | "buy" | "checkout" | "donate" | "order" | "pay" | "plain" | "subscribe";GooglePayButtonColor
type GooglePayButtonColor = "black" | "white";GooglePayShippingOption
interface GooglePayShippingOption {
/** Echoed back in `"shippingoptionchanged"`. */
id: string;
label: string;
description?: string;
/** Smallest currency unit (0 for free shipping). */
amount: number;
}GooglePayShippingAddress
/** Coarse address — no street-level fields until the payment is authorized. */
interface GooglePayShippingAddress {
locality?: string;
administrativeArea?: string;
postalCode?: string;
countryCode?: string;
}GooglePayShippingAddressUpdate
interface GooglePayShippingAddressUpdate {
shippingOptions?: GooglePayShippingOption[];
lineItems?: Array<{ label: string; amount: number }>;
amount?: number;
}GooglePayShippingOptionUpdate
interface GooglePayShippingOptionUpdate {
lineItems?: Array<{ label: string; amount: number }>;
amount?: number;
}GooglePayShippingAddressChangedEvent
interface GooglePayShippingAddressChangedEvent {
address: GooglePayShippingAddress;
resolve: (update: GooglePayShippingAddressUpdate) => void;
reject: () => void;
}GooglePayShippingOptionChangedEvent
interface GooglePayShippingOptionChangedEvent {
shippingOption: GooglePayShippingOption;
resolve: (update: GooglePayShippingOptionUpdate) => void;
reject: () => void;
}GooglePayButtonOptions
interface GooglePayButtonOptions {
/** Smallest currency unit. */
amount: number;
/** ISO 4217, e.g. "VND". */
currency: string;
/** ISO 3166-1 alpha-2, e.g. "VN". */
country: string;
priceLabel?: string;
lineItems?: Array<{ label: string; amount: number }>;
merchantName?: string;
/** Default: "pay". */
type?: GooglePayButtonType;
/** Default: "black". */
color?: GooglePayButtonColor;
/** Default: "en". */
locale?: string;
/** Default: 100% width, 48px height. */
size?: { width?: string | number; height?: string | number };
/** Default: every network. */
allowedCardNetworks?: GooglePayCardNetwork[];
/** Default: PAN_ONLY, CRYPTOGRAM_3DS. */
allowedAuthMethods?: GooglePayAuthMethod[];
requestBillingAddress?: boolean;
requestPayerDetails?: ("email" | "phone")[];
/** Requires a `"shippingaddresschanged"` listener. */
requestShipping?: boolean;
shippingOptions?: GooglePayShippingOption[];
}IGooglePayButtonComponent
interface IGooglePayButtonComponent {
unmount(): void;
/** Returns an unsubscribe function. */
on(
event: "ready" | "success" | "token" | "error" | "cancel" | "shippingaddresschanged" | "shippingoptionchanged",
handler: (data?: unknown) => void,
): () => void;
}Lỗi
Mọi phương thức trả về promise đều reject với một APIError. Bản script không public lớp APIError, nên hãy kiểm tra error.name === "APIError" thay vì instanceof. Hiện các phương thức của SDK không gán code, nên hãy phân nhánh theo status và isUserError. Xem Xử lý lỗi.
APIError
interface APIError extends Error {
readonly name: "APIError";
/** HTTP status, or 0 for client-side and network errors. */
readonly status: number;
/** true when the message is safe to show to the shopper. */
readonly isUserError: boolean;
/** Not set by the Web SDK's methods today — branch on `status` and `isUserError`. */
readonly code?: string;
}