# Stripe Elements

> With Stripe Elements you can offer multiple payment options like *credit card*, *PayPal*, *iDEAL*, *SEPA debit* and many more to your customers.

Stripe Elements provides an interface component (iFrame) that shows several payment options. The payment methods shown to your customer is determined by Stripe based on the currency, the customer’s location and the payment methods you have activated for your Stripe account.

To see which payment methods will be shown to your customers, you can use [Stripe’s Payment Review](https://dashboard.stripe.com/settings/payment_methods/review).

You can also show only [selected payment methods](#optional-show-only-specific-payment-methods).

For detailed information on Stripe Elements, please read the **[Stripe Documentation *Accept a payment*](https://docs.stripe.com/payments/accept-a-payment?payment-ui=elements&api-integration=paymentintents)**.

## Collect payment details

Learn how to create the Stripe Elements interface component, which is used to collect payment information.

### Set up Stripe.js

Add a hidden field called `payment_gateway` with the value `stripe-elements` to the checkout form. This is used by Merx to complete the order.

Add the publishable key as a data attribute to the form element. The publishable key is required to initialize Stripe Elements.

```php title="/site/templates/checkout.php"
<?php
$stripePublishableKey = option('wagnerwagner.merx.production') === true ? option('wagnerwagner.merx.stripe.live.publishable_key') : option('wagnerwagner.merx.stripe.test.publishable_key');
?>
<form id="checkout-form" action="<?= url('api/shop/checkout') ?>" method="post" data-stripe-publishable-key="<?= $stripePublishableKey ?>">
	<!-- Other form fields -->
	<input type="hidden" name="payment_gateway" value="stripe-elements">
	<button>Submit order</button>
</form>
```

Add the `stripe.js` to the head of your HTML file. E.g. in a `head` snippet.

Stripe recommends including Stripe.js on every page of your site to leverage “Stripe’s advanced fraud functionality”. To accept payments, though, the checkout page is enough.

To learn more, see [Including Stripe.js | Stripe JS Reference](https://docs.stripe.com/js/including).

```php title="/site/snippets/head.php"
<?php if ($page->intendedTemplate()->name() === 'checkout'): ?>
  <script src="https://js.stripe.com/clover/stripe.js"></script>
<?php endif ?>
```

To initialize the [Stripe JavaScript API](https://docs.stripe.com/js) you need the publishable key from the checkout form.

```js title="/assets/js/checkout.js"
const checkoutForm = document.getElementById('checkout-form');
const stripePublishableKey = checkoutForm.dataset.stripePublishableKey;
const stripe = Stripe(stripePublishableKey)
```

### Add the Payment Element to your payment page

The Payment Element needs a place to live on your payment page. Create an empty DOM node (container) with a unique ID in your payment form:

```php title="/site/templates/checkout.php"
<form id="checkout-form" action="<?= url('api/shop/checkout') ?>" method="post" data-stripe-publishable-key="<?= $stripePublishableKey ?>">
	<!-- Other form fields -->
	<div id="payment-element">
		<!-- Elements will create form elements here -->
	</div>
	<button>Submit order</button>
</form>
```

### Create and mount the Stripe elements

```js title="/assets/js/checkout.js"
const elements = stripe.elements({
	locale: document.querySelector('html').lang,
});

// Create and mount the Payment Element
const paymentElement = elements.create('payment');
paymentElement.mount('#payment-element');
```

See [Elements Appearance API | Stripe Documentation](https://docs.stripe.com/elements/appearance-api) to learn how to change the look of the elements.

## Submit the payment to Stripe

As described in [Validate checkout](https://merx2.wagnerwagner.de/guide/getting-started/validate-checkout.md) the personal data will be validated first against the personal data fields of the order blueprint. After that the payment data the customer entered into the elements is submitted to and validated by Stripe. 

### Retrieve the client secret

First, you need to retrieve the client secret of the payment intent. The payment intent is created server-side with the currency and the total of the cart. Use [`/shop/stripe-client-secret`](https://merx2.wagnerwagner.de/reference/api/routes/shop-stripe-client-secret-get.md) API endpoint to get the client secret of the payment intent. This secret is used to confirm the payment.

If you followed [Validate checkout](https://merx2.wagnerwagner.de/guide/getting-started/validate-checkout.md), replace `window.location.href = json.redirectUrl;` with the following.

```js title="/assets/js/checkout.js"
  const response = await fetch('/api/shop/stripe-client-secret');
  const { clientSecret } = await response.json();

  const {error} = await stripe.confirmPayment({
    // `Elements` instance that was used to create the Payment Element
    elements,
    clientSecret,
    confirmParams: {
      return_url: json.redirectUrl,
    },
  });

  if (error) {
    // This point will only be reached if there is an immediate error when
    // confirming the payment. Show error to your customer (for example, payment
    // details incomplete)
    checkoutForm.insertBefore(createErrorElement(error.message), event.submitter)
  }
```

> [!CAUTION]
> Fetch the client secret when the customer submits, as above, and not earlier. The payment intent is created with the cart total of that moment, and the cart stays editable until the checkout — a secret fetched on page load pays for a cart the customer has since changed. Merx refuses such a payment with [`error.merx.amountMismatch`](https://merx2.wagnerwagner.de/reference/options/translations/error-merx-amountmismatch.md) rather than shipping goods nobody paid for, so if you do cache the secret, fetch a new one whenever the cart changes.

After completing the payment the customer is redirected to the [order page](https://merx2.wagnerwagner.de/guide/getting-started/displaying-order.md).

Most payment methods confirm the payment right away, so the `stripe-elements` gateway sets the `datePaid` field of the [`OrderPage`](https://merx2.wagnerwagner.de/reference/classes/order-page.md) to the current date in the background, and [`$orderPage->isPaid()`](https://merx2.wagnerwagner.de/reference/classes/order-page/is-paid.md) returns `true`.

For some payment methods like *SEPA Direct Debit*, it can take up to 6 business days until the payment is confirmed. To ensure you don’t fulfill an order before payment clears, it’s recommended to use [webhooks](https://merx2.wagnerwagner.de/guide/configuration/stripe-webhooks.md). You can set them up to automatically notify you, when payments are confirmed.

## Optional: show only specific payment methods

By default Stripe shows the *best* payment methods. You can also define specific payment methods using the [`stripe.paymentIntentParameters`](https://merx2.wagnerwagner.de/reference/options/config-options/stripe-payment-intent-parameters.md) option. 

```php title="/site/config/config.php"
return [
  'wagnerwagner.merx.stripe.paymentIntentParameters' => [
    'payment_method_types' => ['card', 'ideal'],
    'automatic_payment_methods' => ['enabled' => false],
    'capture_method' => 'automatic_async',
    'payment_method_options' => [
      'card' => ['capture_method' => 'manual'],
    ],
  ],
];
```

See Stripe’s API reference [Create a PaymentIntent](https://docs.stripe.com/api/payment_intents/create) to see all parameters. `amount` and `currency` are automatically added to by Merx.

Some payment methods [don’t allow manual capturing](https://docs.stripe.com/payments/capture-later) (e.g. *iDEAL*). If you want to use such payment methods, you need to set [`capture_method`](https://docs.stripe.com/api/payment_intents/create#create_payment_intent-capture_method) to `automatic_async`. You can also set capture method for each payment [using the `payment_method_options`](https://docs.stripe.com/payments/place-a-hold-on-a-payment-method#authorize-only)


> [!NOTE]
> The default options and the options you set in the `config.php` are merged recursively, therefore you need to overwrite/disable `automatic_payment_methods`.
