@clink-ai/clink-elements is currently published at 0.0.3 and the API may still change. Pin the version in package.json.What the full chain looks like
Elements only replaces the frontend segment. Creating the Session still has to happen on the merchant server, because that call uses the Secret Key. Step 1 submits a product identifier, not a final amount computed in the browser. The amount is recalculated server-side in step 2.Who owns what
1. Server: write a wrapper Session endpoint
This layer is not an optional optimization. It is three boundaries at once: the Secret Key security boundary, the amount validation boundary, and the local order association boundary. TheclinkRequest helper below is the authenticated helper from Hosted Checkout; it returns the data field of the response.
uiModeiselementsandreturnUrlis required. Put{ELEMENTS_SESSION_ID}in the URL and Clink substitutes the real Session ID- The field is
returnUrl. Older material calls itredirectUrl, which is wrong —redirectUrlis a response field used inrequires_actionflows merchantReferenceIdis for reconciliation only. It is not an idempotency key; the same value twice produces two Sessions
What Clink returns
Thedata that clinkRequest hands back looks like this:
{ "code": 200, "msg": "success", "data": { ... } } envelope; clinkRequest has already unwrapped it.
expireTime currently comes back as "2026-07-30 12:00:00" — no timezone designator, and not RFC 3339.Do not hand it straight to new Date() or parse it as local time; different runtimes will disagree.Do not infer a “merchant account timezone” or the browser timezone either. The backend currently serializes using the service process default timezone, with no per-account conversion, so the string does not state which zone it belongs to.Precise cross-timezone handling has to wait until the backend upgrades this contract to RFC 3339 with an offset, or to a Unix timestamp. Until then, read status to decide whether a Session can still be paid rather than computing from this string.- The response contains no Publishable Key and no
environment. Both come from the merchant application config — see the next section - The response does contain
url, the hosted checkout address. An Elements integration does not use it
2. Frontend: call the wrapper endpoint, then initialize
The browser does two things: ask the merchant backend for asessionId, then initialize the SDK with it.
PUBLIC_CLINK_PUBLISHABLE_KEY and environment are the application deployment config, taken from the Publishable Key under Developers > API Keys (it starts with pk_uat_). They are safe to expose in the browser, but they do not come from the Create Session response.
- Vite
- Next.js
The SDK parameter is named
publishKey. When this page says Publishable Key, that is the field it means — write publishKey in code.3. Mounting, submitting, and third-party buttons
Two kinds of button can appear on a checkout page, and they behave differently: The merchant pay button — for cards and anything else where the host triggers submission. Callclink.submit() on click.
SDK built-in third-party buttons — Apple Pay, Google Pay, PayPal, and similar. The paymentMethod element renders these internally and handles the click itself. When one takes over, the SDK emits submit-visible: false and the host page hides its own button.
submit-enabled reports whether submission is allowed. Write disabled = !enabled rather than passing the event value straight into disabled, which inverts the logic.
Configure SDK button appearance
Starting with0.0.3, pass sdkButtons under presetOptions to configure Apple Pay, Google Pay, Link, and PayPal buttons.
- Keep button height between
40and55pixels for compatibility across the current button renderers - A non-finite or non-positive height falls back to
45; other unsupported positive values can make the affected button fail to initialize instead of falling back - A non-finite or negative radius falls back to
6; values above half the button height are capped at half the height - Unsupported theme and type values fall back to their defaults
- Every SDK button uses
100%width - When Apple Pay has no explicit theme, light mode uses
blackand dark mode useswhite - Clink applies PayPal height and radius to a wrapper around the PayPal component rather than passing them as PayPal SDK options
loadClinkElements() again.
Track SDK button availability
Listen forsdk-button-initialized before mounting paymentMethod so the first event is not missed.
truemeans the button initialized and is available on the current devicefalsemeans the device is unsupported, the configuration is invalid, the gateway does not support it, the third-party SDK failed to load, or the button exceeded the 10-second timeout for its current initialization cycle- Only buttons present in the current Session are included; the payload is
{}when there are no third-party buttons
4. Events
Two groups, by what they are used for. Update host UI
Flow signals
5. Promo codes
Optional. Creating Coupons and Promotion Codes server-side is covered in Discounts and promotion codes. This section is frontend only.{ amount }, with the fields nested under amount. Writing (info) => info.enablePromotionCode yields undefined, and the promo code entry never appears.
Discounts and the final amount due come from amount. Do not recompute them.
6. Error handling
0.0.3 throws three different kinds of thing, and they are handled differently.
Initialization: loadClinkElements()
It does not throw only ClinkApiError. There are at least three paths:
A response body that is not valid JSON also surfaces as a native
SyntaxError from await res.json().
So treat the catch parameter as unknown and always keep a fallback branch:
After initialization: some errors are thrown synchronously
Not every runtime problem arrives as an event. These are synchronously thrown nativeErrors and need a try/catch:
Errors during the payment flow: events
Errors that belong to the payment flow itself are forwarded from the checkout iframe as events, and are not wrapped in any error class.7. Implementation constraints
Get these wrong and the integration breaks. Everything else about layout is a free design decision.Before shipping
- No Secret Key and no webhook signing key in browser Network traffic or the build output
- The browser only calls the merchant Session wrapper endpoint, never Clink’s Create Session directly
- The backend does not trust a final amount from the browser; it recalculates from the product or cart
- The local order,
merchantReferenceId, andsessionIdmapping is stored - The Publishable Key and
environmentcome from application config, and are not described as Create Session response fields -
submit-visiblehides the host button when Apple Pay, Google Pay, PayPal, or similar appear -
session-successdoes not trigger fulfillment; the final state comes from verified webhooks and backend queries - Old instances are destroyed when the Session changes or the page unmounts
Next
Hosted Checkout
The backend and webhook halves are identical to Hosted Checkout.
Go live
The checklist before switching to production.