# Payment UI (https://prood.com/docs/apps/checkout/payments)

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 [#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) [#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 [#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 [#test-cards]

| Card number | Result |
| --- | --- |
| `4242 4242 4242 4242` | Success |
| `4000 0000 0000 3220` | 3DS required |
| `4000 0000 0000 9995` | Declined |

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

## Easypay (Portugal) [#easypay-portugal]

**Component:** `components/reference-payment.tsx`

Reference-based payment for Portuguese methods:

| Method | UI |
| --- | --- |
| **Multibanco** | Entity + reference number for ATM/homebanking |
| **MB WAY** | Phone number input → push notification |
| **Credit card** | Redirect to Easypay hosted page |

### Flow [#flow-1]

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 [#sandbox]

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

## Ifthenpay (Portugal) [#ifthenpay-portugal]

**Component:** `components/reference-payment.tsx`

Similar reference-based flow:

| Method | Integrations field |
| --- | --- |
| Multibanco | Multibanco key |
| MB WAY | MB WAY key |
| Credit card | Credit card key |

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

## Shared payment page [#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 [#error-handling]

| Error | UI behavior |
| --- | --- |
| Session expired | "Session expired" message with link back to storefront |
| Payment declined | Retry button — state machine allows `failed → payment` |
| Provider error | Error toast with provider message |
| Network error | Retry with exponential backoff |

## Related pages [#related-pages]

<Cards>
  <Card title="Stripe package" href="/docs/packages/payments/stripe" description="@prood/payment-stripe provider." />
  <Card title="Easypay package" href="/docs/packages/payments/easypay" description="@prood/payment-easypay provider." />
  <Card title="Ifthenpay package" href="/docs/packages/payments/ifthenpay" description="@prood/payment-ifthenpay provider." />
  <Card title="Payment integration" href="/docs/guides/payment-integration" description="Setup guide for all providers." />
</Cards>
