ApplicationsCheckout

Payment UI

Stripe Payment Element, Easypay, and Ifthenpay payment interfaces in the checkout app.

The checkout app renders provider-specific payment UI on /c/[id]. The active provider is determined by the session configuration and tenant integration settings.

Provider selection

A session uses one provider: the first the store has connected and turned on, in the order Stripe, Easypay, Ifthenpay. With none connected, it fails with "This store has no payment provider connected yet."

At payment time, @prood/checkout-host rebuilds the provider with the store's own credentials:

ts
const { provider } = await getTenantPaymentProvider(tenantId, session.providerId, { includeDisabled: true })
// credentials come from integration_config, decrypted. There is no environment fallback.

Stripe (embedded Payment Element)

Component: components/stripe-payment.tsx

Uses @stripe/react-stripe-js with the Payment Element:

tsx
<Elements stripe={stripePromise} options={{ clientSecret }}>
  <PaymentElement />
  <Button onClick={handleSubmit}>Pay now</Button>
</Elements>

Flow

  1. Session loaded → submitPayment() creates a Stripe Checkout Session
  2. Client receives clientSecret from payment session
  3. Payment Element renders card form
  4. Customer submits → Stripe handles 3DS if required
  5. On success → redirect to /confirm/{id}

Test cards

Card numberResult
4242 4242 4242 4242Success
4000 0000 0000 32203DS required
4000 0000 0000 9995Declined

The publishable and secret keys are the store's own, entered under Integrations in the dashboard.

Easypay (Portugal)

Component: components/reference-payment.tsx

Reference-based payment for Portuguese methods:

MethodUI
MultibancoEntity + reference number for ATM/homebanking
MB WAYPhone number input → push notification
Credit cardRedirect to Easypay hosted page

Flow

  1. Customer selects payment method
  2. submitPayment({ method: 'multibanco' }) creates Easypay charge
  3. Reference numbers displayed for Multibanco
  4. Customer pays asynchronously (ATM, app)
  5. Easypay webhook confirms payment → order updated

Sandbox

Set the Easypay address field to https://api.test.easypay.pt under Integrations for testing.

Ifthenpay (Portugal)

Component: components/reference-payment.tsx

Similar reference-based flow:

MethodIntegrations field
MultibancoMultibanco key
MB WAYMB WAY key
Credit cardCredit card key

Calls back are verified with the store's anti-phishing key.

Shared payment page

Component: components/payment-page-client.tsx

Orchestrates the payment page:

  1. Loads session snapshot
  2. Determines provider type
  3. Renders Stripe Element or reference payment UI
  4. Handles submit, loading states, and error display
  5. Redirects on success/failure

Error handling

ErrorUI behavior
Session expired"Session expired" message with link back to storefront
Payment declinedRetry button — state machine allows failed → payment
Provider errorError toast with provider message
Network errorRetry with exponential backoff