npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@payabli/component-contracts

v0.6.0

Published

Wire schemas, component registry, and partner-facing types shared by the Payabli embedded components SDK and payhub.

Readme

@payabli/component-contracts

Wire schemas, the component registry, and the partner-facing types that the Payabli embedded components SDK (@payabli/components-web) and the payhub iframe share. One folder per component under src/<component>/.

Install

pnpm add @payabli/component-contracts

What is inside

  • protocol/: the postMessage envelope, parseEnvelope / parseMessage / parsePayload, the base events every component emits, and the messages the SDK sends to the iframe (CONFIG_SET, SUBMIT_REQUEST, ...).
  • payin/: the PayIn component. Its options schema, event schemas, and the PAYMENT_METHODS / CARD_BRANDS vocabulary.
  • registry: register(), getComponentDescriptor(), and the ComponentDefinitions declaration-merge point.
  • ErrorCodes: the error vocabulary shared by the SDK, payhub, and the API.

Schemas are zod v4 mini and the runtime is bundled. Consumers on zod 3.25 or later resolve the types through zod/v4/mini.

PayIn contract summary

Create the session on your server. Pass only presentation settings to the browser. PayIn does not accept prefill in SDK create/update options or React props. Prefill remains a generic capability that other components may support through PREFILL_SET and PREFILL_REQUEST.

Session/init config

The request is POST /api/v2/{slug}/Session/init with platform: 'Web', entryPoint, allowedParentOrigins, and components. The iframe gets each component's config unchanged from the server session. Tenant identity, operation, payment amounts, customer data, and invoice data come only from the server session. The browser submit payload contains payment credentials, capture results, and the selected method, not business configuration.

The API's generated SDK owns session types. Browser payinOptionsSchema contains only showSubmitButton, initialMethod, and methodOrder.

components: [
  {
    type: 'payin',
    config: {
      operation: 'pay',
      methods: ['card', 'ach', 'check', 'applepay', 'googlepay'],
      acceptedCardBrands: ['Visa', 'Mastercard'],
      defaults: { achAccountType: 'checking' },
      ach: { allowedAccountTypes: ['checking', 'savings'] },
      fields: {
        card: {
          holderName: 'required',
          cvv: 'required',
          postalCode: 'required',
        },
      },
      forceCustomerCreation: false,
      paymentDetails: { totalAmount: 12.34, serviceFee: 0.34, currency: 'USD' },
      customerData: { firstName: 'Jane', lastName: 'Doe' },
      invoiceData: { invoiceNumber: 'INV-1001' },
      saveIfSuccess: false,
      idempotencyKey: 'partner-key-123', // Optional. Omit for a generated key.
    },
  },
];
  • paymentDetails is required for pay and auth. totalAmount must exceed zero and already includes serviceFee. Never add the fee again.
  • serviceFee is optional. If present, it must be at least zero and less than totalAmount.
  • currency is optional and accepts only USD or CAD. Omitted currency displays as USD and stays omitted in the API request.
  • customerData is required and non-null for pay and auth. An explicit {} is allowed. invoiceData remains optional. Both pass through unchanged.
  • saveIfSuccess defaults to false. A value of true sets paymentMethod.saveIfSuccess for card, ACH, or wallets. Check submissions reject it.

For tokenize, omit paymentDetails and invoiceData. Do not set saveIfSuccess to true. The session API rejects unknown fields. It does not trim, change case, infer names, or merge addresses in customer or invoice data. The session config rejects amount, billingAddress, and requiredFields. There are no compatibility aliases.

Payment, customer, and invoice data

operation is pay, auth, or tokenize. methods contains only methods supported by the paypoint and operation. An empty methods list uses paypoint capabilities, filtered by the operation. Session creation fails when no supported method remains. acceptedCardBrands stays server-owned. The exported CARD_BRANDS tuple lists the card brand vocabulary. defaults.achAccountType supplies the ACH default. The ACH rules below constrain its value.

These are the supported nested fields. Except for paymentDetails.totalAmount, each nested field is optional. Use the API's generated SDK for session types, not a second browser-side session schema.

paymentDetails: {
  totalAmount: 12.34, serviceFee: 0.34, currency: 'USD',
  checkNumber: '1001', checkUniqueId: 'check-1001',
  orderDescription: 'Order', orderId: '1001', orderIdAlternative: 'ALT-1001',
  paymentDescription: 'Payment', settlementDescriptor: 'STORE', groupNumber: '1',
  unbundled: false,
  categories: [{ label: 'Item', description: 'Item details', amount: 1, qty: 1 }],
  splitFunding: [{
    recipientEntryPoint: 'recipient', accountId: 'account', description: 'Share', amount: 1,
  }],
}

categories[].amount and splitFunding[].amount must exceed zero when present. The displayed total and wallet sheet amount are totalAmount. Wallet currency is currency ?? 'USD'. RDC capture supplies checkNumber, checkUniqueId, and checkImage, overriding configured check identifiers. All other configured payment details survive. The session rejects checkImage, source, and payabliTransId inside paymentDetails. The app keeps its fixed top-level source. It gets entryPoint from the session.

customerData: {
  customerId: 123, customerStatus: 1, firstName: 'Jane', lastName: 'Doe',
  company: 'Example', customerNumber: 'C-1001',
  billingAddress1: '123 Main St', billingAddress2: 'Unit 1', billingCity: 'Austin',
  billingState: 'TX', billingZip: '78701', billingCountry: 'US',
  billingPhone: '5125550100', billingEmail: '[email protected]',
  shippingAddress1: '123 Main St', shippingAddress2: 'Unit 1', shippingCity: 'Austin',
  shippingState: 'TX', shippingZip: '78701', shippingCountry: 'US',
  additionalData: { key: 'value' }, identifierFields: ['email'],
}

An explicit customer or invoice {} stays {}. An absent optional object stays absent in the API request. The app does not infer customer names from holder fields or merge card ZIP codes or wallet contacts into customer data. Unknown properties in these objects and their nested arrays produce a clear HTTP 400 instead of silent removal.

invoiceData: {
  invoiceNumber: 'INV-1001', invoiceDate: '2026-09-10', invoiceDueDate: '2026-10-10',
  invoiceEndDate: '2026-10-10', invoiceStatus: 1, invoiceType: 0,
  frequency: 'onetime', paymentTerms: 'Net 30', termsConditions: 'Terms', notes: 'Note',
  tax: 0, discount: 0, invoiceAmount: 12.34, freightAmount: 0, dutyAmount: 0,
  purchaseOrder: 'PO-1001', firstName: 'Jane', lastName: 'Doe', company: 'Example',
  shippingAddress1: '123 Main St', shippingAddress2: 'Unit 1', shippingCity: 'Austin',
  shippingState: 'TX', shippingZip: '78701', shippingCountry: 'US',
  shippingEmail: '[email protected]', shippingPhone: '5125550100', shippingFromZip: '78701',
  summaryCommodityCode: '1000',
  items: [{
    itemProductCode: 'SKU-1', itemProductName: 'Item', itemDescription: 'Item details',
    itemCommodityCode: '1000', itemUnitOfMeasure: 'each', itemCost: 1, itemQty: 1,
    itemMode: 0, itemCategories: ['Category'], itemTotalAmount: 1, itemTaxAmount: 0, itemTaxRate: 0,
  }],
  attachments: [{
    ftype: 'pdf', filename: 'invoice.pdf', fileDescriptor: 'Invoice',
    furl: 'https://example.com/invoice.pdf', fContent: 'base64',
  }],
  additionalData: { key: 'value' },
}

Customer creation for pay and auth

Pay and auth sessions reject missing or null customer data with customerData is required for pay and auth sessions. The payment API must match the customer through the paypoint identifiers. If it cannot match the customer, it returns HTTP 400 unless forceCustomerCreation: true allows creation. The optional top-level forceCustomerCreation flag defaults to false and applies only to pay and auth. For tokenize, use tokenization.forceCustomerCreation instead. The API rejects the top-level flag for tokenize sessions.

ACH session settings

Set ACH restrictions and request flags in the server session:

config: {
  operation: 'pay',
  methods: ['ach'],
  paymentDetails: { totalAmount: 50, currency: 'USD' },
  customerData: { firstName: 'Jane', lastName: 'Doe' },
  forceCustomerCreation: true,
  defaults: { achAccountType: 'checking' },
  ach: {
    allowedAccountTypes: ['checking'],
    confirmAccountNumber: true,
    confirmRoutingNumber: true,
    validation: true,
    sameDay: true,
  },
}
  • ach is optional for pay and tokenize. Auth sessions reject it because ACH does not support authorization.
  • A non-empty methods list must include ach. Otherwise, the API rejects the block with ach settings require the ach method.
  • allowedAccountTypes defaults to ['checking', 'savings']. Values must be lowercase. The list must contain at least one type without duplicates.
  • defaults.achAccountType is optional and must be an allowed type. With one allowed type, the form uses it without a selector.
  • The server rejects an account type outside the allowed list before any payment API call.

confirmAccountNumber and confirmRoutingNumber default to false. When enabled, each UI-only confirmation field must match its original value. change.isValid includes these checks. The session contains the confirmation flags. Confirmation values never enter browser submit payloads or payment API payloads. The existing ACH consent text remains visible.

ach.validation defaults to false and applies only to pay sessions. It enables the payment API's ACH validation add-on through the GetPaid query flag achValidation. It does not enable local routing-number validation, microdeposits, or Plaid. The connector can also require this add-on. Tokenize sessions reject ach.validation. Use tokenization.achValidation instead.

ach.sameDay defaults to false and applies only to pay sessions through the GetPaid query flag sameDayACH. Tokenize sessions reject it. The payment form has no same-day control. The server sends these query flags only when true and only for ACH pay submissions. The server sends forceCustomerCreation only when true, for any pay method or an auth submission.

Field visibility and requiredness

Set fields in the server session config, not in browser options:

fields: {
  card: {
    holderName: 'required',
    cvv: 'optional',
    postalCode: 'hidden',
  },
  ach: { holderName: 'optional' },
  check: { holderName: 'required' },
}

The fields block, each method block, and every entry are optional. Every entry defaults to required. Values must be lowercase required, optional, or hidden.

  • required preserves the existing required input and validation rules.
  • optional shows the input and allows an empty value. The API payload omits empty credentials instead of sending "".
  • Non-empty optional values use the existing format rules, including three or four digits for CVV and the postal code format.
  • hidden removes the input from the form, client validation, and change.fields. The API payload omits the credential key.
  • The server rejects a non-empty browser submit value for a hidden field. Session rules also control server validation and change.isValid.

Card number, expiration, routing number, account number, and account type cannot be configured or hidden through fields. The API rejects unknown members. The typed fields block replaces requiredFields, with no compatibility alias. If a non-empty methods list excludes a method, the API rejects its field block with fields.<method> requires the <method> method. Check is available only for pay. Tokenize sessions use the same field rules for card and ACH. Wallets are unaffected.

The playground shows a method's field controls only when that method is checked. It sends only non-default entries and omits empty method blocks and an empty fields block.

Tokenization

Create tokenization configuration on your server with Session/init:

components: [
  {
    type: 'payin',
    config: {
      operation: 'tokenize',
      methods: ['card', 'ach'],
      customerData: { customerId: 123 },
      tokenization: {
        lifetime: 'permanent', // Required: 'temporary' or 'permanent'.
        customerMode: 'customer', // Required: 'anonymous' or 'customer'.
        forceCustomerCreation: false, // Optional. Defaults to false.
        achValidation: false, // Optional. Defaults to false.
        methodDescription: 'Saved payment method', // Optional, 1 to 255 characters.
        methodType: 0, // Optional: 0, 1, or 2. Omit for the connector default.
      },
    },
  },
];
  • anonymous mode forbids customerData. Omit forceCustomerCreation or set it to false.
  • customer mode requires non-null customerData. The API must resolve the customer, or forceCustomerCreation: true must create it.
  • temporary methods expire after 12 hours. Use permanent for storage without that expiration.
  • achValidation: true requires ACH in methods and enables the API validation add-on. The connector can also require this add-on.
  • methodType values are 0 for single use, 1 for a universal token, and 2 for a network token. Unsupported connector types return an error. The API stores methodDescription unchanged.

methods accepts only card and ach. An empty list uses the paypoint's methods, filtered to card and ACH. Session creation fails if no supported method remains. Wallet and check tokenization are not available. Fallback authorization is not offered because the API ignores fallbackAuth and fallbackAuthAmount.

tokenization is required for tokenize and rejected for pay or auth. Omit paymentDetails, invoiceData, and saveIfSuccess for tokenize sessions. The component shows no payment total. Its default submit label is Save, unless appearance.labels.submit overrides it.

Idempotency key

idempotencyKey is optional in components[].config. Supply it from your server, or omit it for a key generated by platform-api. A supplied key must contain 1 to 64 characters from [A-Za-z0-9._:-]. The API stores it as {sessionId}:{key}.

Browser options

options contains only the following browser presentation settings:

{
  showSubmitButton?: boolean;
  initialMethod?: 'card' | 'ach' | 'check';
  methodOrder?: ('card' | 'ach' | 'check')[];
}

showSubmitButton: true shows the internal submit button. Set it to false when the host calls submit().

initialMethod and methodOrder are optional browser options, alongside showSubmitButton. Both accept only card, ach, and check. methodOrder must be an array. The exported FORM_METHODS tuple defines the default order.

  1. Use only session-allowed form methods. Options never enable a method.
  2. Keep allowed methodOrder entries in the given order. The first occurrence wins. Ignore duplicates and disallowed entries.
  3. Append unlisted allowed methods in the default order: card, ach, check.
  4. Select initialMethod when session-allowed. Otherwise, select the first method in the effective order.
  5. Keep wallets in their own section. These options do not affect wallets.

Without overrides, the default order and initial method stay unchanged. The options schema rejects unknown method names and non-array orders. It accepts duplicates for the iframe to ignore. The SDK forwards options unchanged through CONFIG_SET.options. React uses the existing options prop, with no new props. payin.update({ options: { initialMethod: 'card', methodOrder: ['card', 'ach'] } }) sends CONFIG_SET to the live frame. The frame stays in place. No new session is required. Layout, density, label style, and color scheme are unchanged.

Appearance

appearance is client-side. update() applies it to the live frame without a new session. locale is part of the frame URL, so update() does not change it on a live frame.

| Setting | Values and behavior | | ------------------- | ------------------------------------------------------------------------------------------------------------ | | layout | tabs, accordion, stacked, minimal. Default is tabs. A single-method session renders the bare form. | | layoutVariant | Density: compact, standard, wide. | | labelStyle | above, floating, outlined. Default is above. | | colorScheme | light, dark, system. system follows the buyer's device. | | tokens / labels | String records for supported CSS custom properties and text overrides. |

above places labels over inputs. floating lifts labels inside the box. outlined lifts labels onto a notch in the border. Supported tokens include --primary, --background, --pb-container-padding, and --pb-container-background.

The SDK matches the iframe's color-scheme to its document. A mismatch makes the cross-origin frame opaque. Use --pb-container-background: transparent to show the host page through the frame in both modes.

Text overrides

Set field text in appearance.labels. These browser settings do not change payloads, validation, or session-allowed methods.

| Label key | Placeholder key | | -------------------------------- | ----------------------------------------- | | label.card.holderName | placeholder.card.holderName | | label.card.number | placeholder.card.number | | label.card.expiration | placeholder.card.expiration | | label.card.cvv | placeholder.card.cvv | | label.card.postalCode | placeholder.card.postalCode | | label.ach.holderName | placeholder.ach.holderName | | label.ach.accountType | None | | label.ach.routingNumber | placeholder.ach.routingNumber | | label.ach.accountNumber | placeholder.ach.accountNumber | | label.ach.confirmRoutingNumber | placeholder.ach.confirmRoutingNumber | | label.ach.confirmAccountNumber | placeholder.ach.confirmAccountNumber | | label.check.holderName | placeholder.check.holderName | | label.check.capture | None, sets the RDC capture call to action |

method-check sets the check tab label. The existing keys remain available:

submit
method-card
method-ach
method-applepay
method-googlepay
wallet-divider
ach-consent
error-declined
error-generic

The iframe drops keys outside this allowlist. It trims values and limits them to 120 characters. It renders values as text with React escaping, never as HTML. The shared wire schema remains Record<string, string> because other components use different keys. Use the exported PAYIN_LABEL_KEYS tuple and PayInLabelKey type from @payabli/component-contracts for PayIn-specific typing:

import type { PayInLabelKey } from '@payabli/component-contracts';

const labels: Partial<Record<PayInLabelKey, string>> = {
  'label.card.number': 'Card number',
  'placeholder.card.number': 'Enter card number',
  'method-check': 'Scan a check',
};

const payin = payabli.create('payin', {
  appearance: { labels },
  options: {
    initialMethod: 'ach',
    methodOrder: ['ach', 'card', 'check'],
  },
});

Events

Success and errors

success and React's onSuccess report confirmed success only. submit() resolves with the same result data.

[!IMPORTANT] Confirm every outcome on your server with a webhook or a status call before you fulfill an order. Do not use the browser success event alone as proof of payment. Code on the page can fake it.

The payload is PayInSuccessPayload, one closed shape for every operation:

{
  operation: 'pay' | 'auth' | 'tokenize',
  method: 'card' | 'ach' | 'check' | 'applepay' | 'googlepay',
  transactionId?: string,   // pay, auth
  storedMethodId?: string,  // tokenize; pay or auth that saved the method
  customerId?: number,      // when the API created or matched a customer
  paymentMethod?: { maskedAccount?, accountType?, expiration?, holderName? },
  amount?: { total: number, serviceFee?: number, currency?: string }, // pay, auth
}

transactionId is the Payabli transaction id. storedMethodId is the saved payment method id: pass it back as storedMethodId on a later payment. The API response body does not reach the page.

A failed SUBMIT_RESULT rejects submit() and emits error, mapped to React's onError. Errors have { code, message, recoverable, details? }. The SDK forwards the code and message unchanged. Wallet helpers report success only when the server classifies the result as successful. The iframe emits failed SUBMIT_RESULT and ERROR with the same error once per wallet failure. Recoverable failures release the lock. Non-recoverable failures keep it locked.

| Code | Meaning and action | | ------------------------- | ----------------------------------------------------------------------------------------------------------- | | SUBMIT_FAILED | Confirmed decline or validation failure. Keep entered values and allow correction when recoverable: true. | | OUTCOME_UNKNOWN | The API did not confirm the result. A prior attempt may have processed. Do not retry this session. | | SESSION_COMPLETED | This component already succeeded. The SDK emits error and removes this component. | | UNSUPPORTED_OPERATION | The session operation does not support this method. No payment API call occurs. | | UNSUPPORTED_SAVE_METHOD | Check cannot use saveIfSuccess: true. No payment API call occurs. |

The server can report submit codes beyond the SDK's exported ErrorCodes constants, including SUBMIT_FAILED and UNSUPPORTED_SAVE_METHOD.

Lifecycle and wallet cancellation

WALLET_CANCEL maps to the SDK event walletCancel and the React callback onWalletCancel. The payload is { wallet: 'applepay' | 'googlepay' }. The iframe emits it when the buyer closes the Apple Pay sheet or Google Pay returns statusCode: "CANCELED". Cancellation is not an error or a submit result. The iframe releases its lock, so the buyer can try another payment method. The SDK does not emit error or success, or settle a pending submit() promise, for this event. The message event also reports the inbound WALLET_CANCEL for the playground log.

payin.on('walletCancel', ({ wallet }) => {
  // Update the checkout UI for the canceled wallet.
});
<PayIn
  onWalletCancel={({ wallet }) => {
    // Update the checkout UI for the canceled wallet.
  }}
/>

ready and rendered are lifecycle events. Validity is change.isValid (STATE_CHANGE). In React, read isValid from onChange. Do not use onReady or onRendered as a validity signal.

SDK and session errors

| Code | Meaning and recovery | | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | LOAD_TIMEOUT | The iframe did not send READY within the timeout. The SDK retries loading once before reporting an error. | | PROTOCOL_ERROR | postMessage validation failed. | | INVALID_STATE | The operation targets a destroyed instance. | | EXCHANGE_CODE_INVALID | The frame could not redeem its exchange code: unknown, expired, used, or issued for another key. The SDK emits error and removes this component. Create a new session. |

Session errors go to payabli.on('sessionExpired') or payabli.on('sessionError'), not to the component error event.

| Code | Event | SDK action and recovery | | ---------------------------- | ---------------- | -------------------------------------------------------------------------------------------------------- | | SESSION_EXPIRED | sessionExpired | Removes every frame. Create a new session and mount again. | | RENDER_TOKEN_EXPIRED | sessionExpired | Removes every frame. The render token ends at the session ceiling. Create a new session and mount again. | | SESSION_REVOKED | sessionError | Removes every frame. Create a new session and mount again. | | SESSION_COOKIE_UNAVAILABLE | sessionError | Removes every frame. The browser did not return the session cookie to the frame. | | SESSION_LIMIT_REACHED | sessionError | Removes every frame. Create a new session. | | SESSION_INVALID | sessionError | Removes every frame. Create a new session. | | ORIGIN_NOT_ALLOWED | sessionError | Removes every frame. Add the page origin to allowedParentOrigins and create a new session. | | RENDER_TOKEN_INVALID | sessionError | Removes every frame. Render token signature, audience, or format checks failed. Create a new session. |

Post-success lock and session replacement

The API namespaces a partner key as {sessionId}:{key} so two sessions can reuse the same partner key. A generated key uses the same form: {sessionId}:{GUID}, 65 characters in total. Attempt 1 sends the stored value returned by bootstrap, and later attempts append -{n} to that value. The attempt counter advances only after a confirmed failure. Browser submissions do not supply the key or the attempt number.

OUTCOME_UNKNOWN means the payment API did not confirm the outcome. A previous attempt may have been processed. This error is terminal for the session (recoverable: false). The component keeps entered values but stays locked. The host must not retry with this session. A fresh session is required. The SDK never retries a submit automatically. SUBMIT_FAILED remains the code for confirmed failures.

After success, the component clears sensitive payment fields and locks all fields and wallet buttons. Repeated submissions return SESSION_COMPLETED with recoverable: false. Both error codes pass unchanged to the submit() rejection and the error event, or React's onError callback.

Completion is per component key. After a successful PayIn submit, the SDK emits success and then removes only that PayIn component. Other components of the session stay usable, including another PayIn with a different key. Render the next state of your page in the success handler. To take another payment, create a new session and a new Payabli instance.

Wallet sheets and form submission share one in-flight lock. Closing a wallet sheet releases it. After success, the iframe clears card number, CVV, expiration, account number, routing number, and check capture. The server records session completion and rejects later submissions before any payment API call. Appearance updates do not clear the server completion lock.

The server stores attempt counters and completion records in Redis for the session lifetime. If Redis is unavailable, submission fails closed. GetPaid and Authorize cache each idempotency key for two minutes before processing, regardless of the result. Duplicate keys return HTTP 409 without replaying the original result. HTTP 409 maps to OUTCOME_UNKNOWN, not a decline. Network failures, timeouts after dispatch, and unconfirmed server failures also map to OUTCOME_UNKNOWN. TokenStorage does not deduplicate this header. The session attempt and completion locks still apply to tokenization.

Method × operation matrix

| method | pay (GetPaid) | auth (Authorize) | tokenize (TokenStorage) | | --------- | ------------- | ---------------- | ----------------------- | | card | yes | yes | yes | | ach | yes | NO | yes | | check | yes | NO | NO | | applepay | yes | yes | NO | | googlepay | yes | yes | NO |

Session/init rejects method and operation pairs outside this table. For auth, an empty methods list uses paypoint capabilities filtered to card, Apple Pay, and Google Pay. Session creation fails if no supported method remains. The iframe also rejects unsupported pairs before an API call. It never substitutes pay for auth or tokenize. UNSUPPORTED_OPERATION means: "The session operation does not support this payment method."

Migration from the legacy embedded component

This is a pre-production breaking change. Remove obsolete configuration instead of adding compatibility aliases.

| Legacy option or behavior | New location or action | | ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | | Browser operation, methods, acceptedCardBrands | Server components[].config in Session/init. | | amount: { amount, currency } | config.paymentDetails: { totalAmount, currency }. The total already includes the fee. | | billingAddress | Removed. Supply customer billing fields in server-owned config.customerData. | | requiredFields | Removed. Use typed config.fields entries. | | PayIn prefill | Removed from SDK options and React props. Supply customer data in the server session. | | Customer, invoice, and save-on-success data in browser config | config.customerData, config.invoiceData, config.saveIfSuccess. | | Form selection and order | Browser options.initialMethod and options.methodOrder. Only session-allowed methods appear. | | Field label and placeholder text | Browser appearance.labels using the keys above. | | appearance.mode | appearance.colorScheme. | | Ready-means-valid behavior | Use change.isValid or React onChange. ready and rendered are lifecycle events. | | Repeated submit after success | Create a new session and a new Payabli instance. |

Pay and auth examples must supply non-null customerData. Use identifiers that match the paypoint's customer rules. An empty object passes session validation but does not guarantee that the payment API can resolve a customer. Do not send customer or invoice data from buyer-controlled browser configuration.

Rollout order

  1. Deploy platform-api so Session/init accepts and returns the new PayIn config. The bootstrap must carry that config.
  2. Publish @payabli/component-contracts, the @payabli/components-web CDN script, and @payabli/components-react. The SDK must support WALLET_CANCEL, the new error codes, and the initialMethod and methodOrder options.
  3. Deploy payabli/app with session-owned submission, field rules, tokenization, wallet classification, and locks. Remove its temporary local contract shims in a follow-up.
  4. Update playground and example installations to use those releases.

Publish the SDK before deploying the iframe because an old SDK turns an unknown WALLET_CANCEL event into PROTOCOL_ERROR.

The follow-up removes these temporary local contract shims:

  • Error literals in app/modules/embed/payin-errors.ts.
  • The local WALLET_CANCEL schema and options extension in meta.ts.
  • The zod-v4 alias.

Deferred items

| Area | Not included in this contract | | ---------------- | --------------------------------------------------------------------------------------------------------------------------- | | Payment flows | Saved-method selection, recurring payments, cloud devices, and lightbox. | | GetPaid metadata | Top-level orderId, orderDescription, and configurable source. Nested paymentDetails order fields remain supported. | | Tokenization | Wallet and check tokenization. Wallet storage needs a product decision about the API's hidden $0.01 authorization and void. | | Styling | External CSS and per-field layout. Use supported appearance tokens, layouts, and text keys. |

Fallback authorization is not offered. The API ignores fallbackAuth and fallbackAuthAmount. Anonymous wallet storage also has an unresolved server failure. Do not bypass the wallet tokenization restriction.

Versioning

Pre-1.0. A breaking wire change is a minor bump. MESSAGE_VERSION changes only when old and new sides can no longer talk.