PHPackages                             programmatordev/kirby-stripe-checkout - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Payment Processing](/categories/payments)
4. /
5. programmatordev/kirby-stripe-checkout

ActiveKirby-plugin[Payment Processing](/categories/payments)

programmatordev/kirby-stripe-checkout
=====================================

Stripe Checkout for Kirby CMS

0.6.2(9mo ago)26MITPHPPHP &gt;=8.2.0CI passing

Since Dec 16Pushed 9mo agoCompare

[ Source](https://github.com/programmatordev/kirby-stripe-checkout)[ Packagist](https://packagist.org/packages/programmatordev/kirby-stripe-checkout)[ Docs](https://github.com/programmatordev/kirby-stripe-checkout)[ RSS](/packages/programmatordev-kirby-stripe-checkout/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (8)Dependencies (8)Versions (8)Used By (0)

Kirby Stripe Checkout
=====================

[](#kirby-stripe-checkout)

[![Latest Version](https://camo.githubusercontent.com/4f3c914d119fc24907fb259c27569088aec4184164989c4844b71a048212a517/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f72656c656173652f70726f6772616d6d61746f726465762f6b697262792d7374726970652d636865636b6f75742e7376673f7374796c653d666c61742d737175617265)](https://github.com/programmatordev/kirby-stripe-checkout/releases)[![Software License](https://camo.githubusercontent.com/55c0218c8f8009f06ad4ddae837ddd05301481fcf0dff8e0ed9dadda8780713e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d627269676874677265656e2e7376673f7374796c653d666c61742d737175617265)](LICENSE)[![Tests](https://github.com/programmatordev/kirby-stripe-checkout/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/programmatordev/kirby-stripe-checkout/actions/workflows/ci.yml?query=branch%3Amain)

[Stripe Checkout](https://stripe.com/en-pt/payments/checkout) for [Kirby CMS](https://getkirby.com).

Caution

This plugin is still in its early stages. This means that it should not be considered stable, so use it at your own risk. Expect a lot of breaking changes until version `1.0`.

Features
--------

[](#features)

- 🔥 Stripe Checkout for both `hosted` and `embedded` modes;
- 💸 Handles sync and async payments (credit card, bank transfer, etc.);
- 📦 Orders panel page;
- ⚙️ Checkout Settings panel page;
- 🪝 Hooks for all payment status (completed, pending and failed), orders and checkout sessions;
- 🛒 Cart management;
- ...and more.

Documentation
-------------

[](#documentation)

- [Requirements](#requirements)
- [Installation](#installation)
- [Options](#options)
- [Hooks](#hooks)
- [Cart](#cart)
- [Translations](#translations-1)
- [Setup](#setup)
- [Development](#development)
- [Production](#production)

Requirements
------------

[](#requirements)

- PHP `8.2` or higher;
- Kirby CMS `4.0` or higher;
- [Stripe account](https://dashboard.stripe.com/register).

Installation
------------

[](#installation)

Install the plugin via [Composer](https://getcomposer.org/):

```
composer require programmatordev/kirby-stripe-checkout
```

Options
-------

[](#options)

Default options:

```
// config.php

return [
    'programmatordev.stripe-checkout' => [
        'stripePublicKey' => null,
        'stripeSecretKey' => null,
        'stripeWebhookSecret' => null,
        'currency' => 'EUR',
        'uiMode' => 'hosted',
        'successPage' => null,
        'cancelPage' => null,
        'returnPage' => null,
        'ordersPage' => 'orders',
        'settingsPage' => 'checkout-settings',
        'cartSnippet' => null,
        'translations' => []
    ]
];
```

Tip

It is recommended that you use a library that enables environment variables to store your project credentials in a separate place from your code and to have separate development and production access keys.

List of all available options:

- [stripePublicKey](#stripepublickey)
- [stripeSecretKey](#stripesecretkey)
- [stripeWebhookSecret](#stripewebhooksecret)
- [uiMode](#uimode)
- [currency](#currency)
- [successPage](#successpage)
- [cancelPage](#cancelpage)
- [returnPage](#returnpage)
- [ordersPage](#orderspage)
- [settingsPage](#settingspage)
- [cartSnippet](#cartsnippet)
- [translations](#translations)

### `stripePublicKey`

[](#stripepublickey)

type: `string` `required`

Stripe public key found on the Stripe Dashboard.

### `stripeSecretKey`

[](#stripesecretkey)

type: `string` `required`

Stripe secret key found on the Stripe Dashboard.

### `stripeWebhookSecret`

[](#stripewebhooksecret)

type: `string` `required`

Webhook secret found when a Webhook is created in the Stripe Dashboard.

Check the [Setup](#setup) section for more information.

### `currency`

[](#currency)

type: `string` default: `EUR` `required`

Three-letter [ISO currency code](https://www.iso.org/iso-4217-currency-codes.html). Must be a [supported currency](https://stripe.com/docs/currencies).

### `uiMode`

[](#uimode)

type: `string` default: `hosted` `required`

The UI mode of the Checkout Session.

Available options:

- `hosted` the Checkout Session will be displayed on a Stripe-hosted page (where the user will be redirected);
- `embedded` the Checkout Session will be displayed as an embedded form on the website page.

### `successPage`

[](#successpage)

type: `string`

This option is `required` if `uiMode` is set to `hosted`.

Page to where a user will be redirected when a Checkout Session is completed (form successfully submitted).

Must be a valid Kirby page `id`. The `id` is used, instead of a URL, to make sure that the user is redirected correctly in case of multi-language setups.

### `cancelPage`

[](#cancelpage)

type: `string`

This option is `required` if `uiMode` is set to `hosted`.

Page to where a user will be redirected if decides to cancel the payment and return to the website.

Must be a valid Kirby page `id`. The `id` is used, instead of a URL, to make sure that the user is redirected correctly in case of multi-language setups.

### `returnPage`

[](#returnpage)

type: `string`

This option is `required` if `uiMode` is set to `embedded`.

Page to where a user will be redirected when a Checkout Session is completed (form successfully submitted).

Must be a valid Kirby page `id`. The `id` is used, instead of a URL, to make sure that the user is redirected correctly in case of multi-language setups.

### `ordersPage`

[](#orderspage)

type: `string` default: `orders` `required`

Kirby Panel page with the overview of all orders.

Must be a valid Kirby page `id`.

Check the [Setup](#setup) section for more information.

### `settingsPage`

[](#settingspage)

type: `string` default: `checkout-settings` `required`

Kirby Panel page with Checkout settings.

Must be a valid Kirby page `id`.

Check the [Setup](#setup) section for more information.

### `cartSnippet`

[](#cartsnippet)

type: `?string` default: `null`

When set, it will look for the snippet with the same name and return the HTML content on every cart API response. Useful when adding, updating or removing cart contents, and you want to update the HTML on every request.

If snippet does not exist or is empty, it will return `null`.

### `translations`

[](#translations)

type: `array` default: `[]`

Use this option to overwrite existing translations or to add a new one that is not bundled with the plugin. Check the [`translations`](translations) folder for all available translations.

An example when overwriting an existing translation:

```
// site/config/config.php

return [
    'programmatordev.stripe-checkout' => [
        'translations' => [
            'en' => [
                // overwrites "Orders" to "Tickets"
                'stripe-checkout.fields.orders.ordersHeadline.label' => 'Tickets'
            ]
        ]
    ]
];
```

If a translation does not exist, you can provide yours:

```
// site/config/config.php

return [
    'programmatordev.stripe-checkout.translations' => [
        'translations' => [
            // The German translation is not currently bundled with the plugin, so you can provide your own
            'de' => [
                'stripe-checkout.fields.product.price.label' => 'Preis',
                'stripe-checkout.fields.product.thumbnail.label' => 'Vorschaubild',
                'stripe-checkout.fields.settings.shippingHeadline.label' => 'Versand',
                'stripe-checkout.fields.settings.shippingEnabled.label' => 'Versandeinstellungen',
                'stripe-checkout.fields.settings.shippingAllowedCountries.label' => 'Erlaubte Länder',
                // ...
            ]
        ]
    ]
];
```

Hooks
-----

[](#hooks)

- [stripe-checkout.session.create:before](#stripe-checkoutsessioncreatebefore)
- [stripe-checkout.order.create:before](#stripe-checkoutordercreatebefore)
- [stripe-checkout.payment:succeeded](#stripe-checkoutpaymentsucceeded)
- [stripe-checkout.payment:pending](#stripe-checkoutpaymentpending)
- [stripe-checkout.payment:failed](#stripe-checkoutpaymentfailed)

### `stripe-checkout.session.create:before`

[](#stripe-checkoutsessioncreatebefore)

Triggered before creating a Checkout Session. Useful to set additional Checkout Session parameters.

You can check all the available parameters in the Stripe API [documentation page](https://docs.stripe.com/api/checkout/sessions/create?lang=php).

```
// config.php

return [
    'hooks' => [
        'stripe-checkout.session.create:before' => function (array $sessionParams): array
        {
            // for example, if you want to enable promotion codes
            // https://docs.stripe.com/api/checkout/sessions/create?lang=php#create_checkout_session-allow_promotion_codes
            $sessionParams['allow_promotion_codes'] = true;

            return $sessionParams;
        }
    ]
];
```

Warning

Take into account that the `sessionParams` variable contains data required to initialize a Checkout Session. You may change these but at your own risk.

### `stripe-checkout.order.create:before`

[](#stripe-checkoutordercreatebefore)

Triggered before creating an Order page in the Panel. Useful to set additional Order data in case you add additional fields in the blueprint or want to change existing ones.

```
// config.php

use Stripe\Checkout\Session;
use Stripe\Event;

return [
    'hooks' => [
        'stripe-checkout.order.create:before' => function (array $orderContent, Session $checkoutSession, Event $stripeEvent): array
        {
            // change order content
            // ...

            return $orderContent;
        }
    ]
];
```

Warning

Take into account that the `orderContent` variable contains all data required to create an Order page. You may change these but at your own risk.

### `stripe-checkout.payment:succeeded`

[](#stripe-checkoutpaymentsucceeded)

Triggered when a payment is completed successfully.

Important

This hook is triggered for both sync and async payments. An example of a sync payment is when a customer pays using a credit card as the payment method. An example of an async payment is when a customer wants to pay through a bank transfer. In this case, the hook will only be triggered when the actual bank transfer is successfully performed. Check the [stripe-checkout.payment:pending](#stripe-checkoutpaymentpending) hook to handle pending payments.

```
// config.php

use Kirby\Cms\Page;
use Stripe\Checkout\Session;
use Stripe\Event;

return [
    'hooks' => [
        'stripe-checkout.payment:succeeded' => function (Page $orderPage, Session $checkoutSession, Event $stripeEvent): void
        {
            // email the customer when the payment succeeds
            kirby()->email([
                'from' => 'orders@myshop.com',
                'to' => $orderPage->customer()->toObject()->email()->value(),
                'subject' => 'Thank you for your order!',
                'body' => 'Your order will be processed soon.',
            ]);
        }
    ]
];
```

### `stripe-checkout.payment:pending`

[](#stripe-checkoutpaymentpending)

Triggered when an order is pending payment.

This happens when a customer uses an async payment method, like a bank transfer, where the Checkout form is submitted successfully, but the payment is yet to be made.

```
// config.php

use Kirby\Cms\Page;
use Stripe\Checkout\Session;
use Stripe\Event;

return [
    'hooks' => [
        'stripe-checkout.payment:pending' => function (Page $orderPage, Session $checkoutSession, Event $stripeEvent): void
        {
            // email the customer when the payment is pending
            kirby()->email([
                'from' => 'orders@myshop.com',
                'to' => $orderPage->customer()->toObject()->email()->value(),
                'subject' => 'Thank you for your order!',
                'body' => 'Your order is pending payment.',
            ]);
        }
    ]
];
```

### `stripe-checkout.payment:failed`

[](#stripe-checkoutpaymentfailed)

Triggered when a payment has failed.

This happens when a customer uses an async payment method, like a bank transfer, where the Checkout form is submitted successfully, but the payment has failed (for example, the deadline for the payment has expired).

```
// config.php

use Kirby\Cms\Page;
use Stripe\Checkout\Session;
use Stripe\Event;

return [
    'hooks' => [
        'stripe-checkout.payment:failed' => function (Page $orderPage, Session $checkoutSession, Event $stripeEvent): void
        {
            // email the customer when the payment has failed
            kirby()->email([
                'from' => 'orders@myshop.com',
                'to' => $orderPage->customer()->toObject()->email()->value(),
                'subject' => 'Bad news!',
                'body' => 'Your order has been canceled because the payment has failed.',
            ]);
        }
    ]
];
```

Cart
----

[](#cart)

A cart management system already exists and is required to be able to create a Checkout Session. The reason for this is that the checkout line items are generated based on the current cart contents. This means that the cart must have at least one added item; otherwise it will throw an error.

### PHP

[](#php)

A `cart()` function is available to manage the cart contents.

```
use ProgrammatorDev\StripeCheckout\Cart\Cart;

cart(array $options = []): Cart
```

Default options:

```
cart([
    // the same as configured in the plugin options
    'currency' => option('programmatordev.stripe-checkout.currency'),
    'cartSnippet' => option('programmatordev.stripe-checkout.cartSnippet')
]);
```

Available methods:

- [addItem](#additem)
- [updateItem](#updateitem)
- [removeItem](#removeitem)
- [items](#items)
- [totalQuantity](#totalquantity)
- [totalAmount](#totalamount)
- [currency](#currency-1)
- [currencySymbol](#currencysymbol)
- [cartSnippet](#getcartsnippet)
- [destroy](#destroy)
- [toArray](#toarray)

#### `addItem`

[](#additem)

```
addItem(string $id, int $quantity, ?array $options = null): string
```

Adds an item to the cart.

The `id` and `quantity` values are required. An additional `options` value is available to set the options of a product (color, size, etc.).

Important to note that the `id` must be a valid Kirby page id and the page must include a valid `price` field. Otherwise, an exception will be thrown. Check the [Setup](#setup) section for more information.

Information related to the `price`, `name` and `thumbnail` are added to the item based on the given `id` (and related Kirby page).

If the item that is being added already exists in the cart, the sum of its quantities will be made into a single item.

If the same item is added but with different options, it will be considered different items in the cart. For example, a t-shirt with the color blue and the same t-shirt with the color red will be different items.

A `key` is returned that uniquely identifies the item in the cart.

```
$cart = cart();

// a key is returned and uniquely identifies that item in the cart
$key = $cart->addItem(id: 'products/cd', quantity: 1);

// you can add options per item
$key = $cart->addItem(
    id: 'products/t-shirt',
    quantity: 1,
    options: ['Color' => 'Green', 'Size' => 'Medium']
);
```

#### `updateItem`

[](#updateitem)

```
updateItem(string $key, int $quantity): void
```

Updates the `quantity` of an item in the cart.

```
$cart = cart();

$key = $cart->addItem(id: 'products/cd', quantity: 1);

$cart->updateItem(key: $key, quantity: 3);
```

#### `removeItem`

[](#removeitem)

```
removeItem(string $key): void
```

Removes an item from the cart.

```
$cart = cart();

$key = $cart->addItem(id: 'products/cd', quantity: 1);

$cart->removeItem($key);
```

#### `items`

[](#items)

```
use Kirby\Toolkit\Collection;
use ProgrammatorDev\StripeCheckout\Cart\Item

/** @return Collection */
items(): Collection
```

Collection with all items in the cart.

```
use ProgrammatorDev\StripeCheckout\Cart\Item;

$cart = cart();
$items = $cart->items();

/** @var Item $item */
foreach ($items as $key => $item) {
    $item->id();
    $item->quantity()
    $item->options();
    $item->productPage();
    $item->name();
    $item->price();
    $item->totalAmount();
    $item->thumbnail();
}
```

#### `totalQuantity`

[](#totalquantity)

```
totalQuantity(): int
```

Get the total quantity of items in the cart.

```
$cart = cart();
echo $cart->totalQuantity(); // 3
```

#### `totalAmount`

[](#totalamount)

```
totalAmount(): int|float
```

Get the total amount in the cart.

```
$cart = cart();
echo $cart->totalAmount(); // 100
```

#### `currency`

[](#currency-1)

```
currency(): string
```

Get currency.

```
$cart = cart();
echo $cart->currency(); // EUR
```

#### `currencySymbol`

[](#currencysymbol)

```
currencySymbol(): string
```

Get currency symbol.

```
$cart = cart();
echo $cart->currencySymbol(); // €
```

#### `cartSnippet`

[](#cartsnippet-1)

```
cartSnippet(bool $render = false): ?string
```

Get cart snippet if set in the [`cartSnippet`](#cartsnippet) option.

if `render` is set to `true` it will return the rendered HTML snippet

```
$cart = cart();
echo $cart->cartSnippet(render: false); // snippet name or null
echo $cart->cartSnippet(render: true); // rendered snippet HTML
```

#### `destroy`

[](#destroy)

```
destroy(): void
```

Destroy all contents and reset the cart to the initial state.

```
$cart = cart();
$cart->destroy();
```

#### `toArray`

[](#toarray)

```
toArray(bool $includeCurrency = true): array
```

Converts all cart contents into an array.

```
$cart = cart();
$cart->toArray();
```

### JavaScript

[](#javascript)

A JavaScript library is currently being developed. Meanwhile, check the [API endpoints](#api-endpoints) section below for examples on how to use with JavaScript.

### API endpoints

[](#api-endpoints)

Endpoints are available to help manage the cart system in the frontend. You can make requests to these to add, update and remove items, get the cart contents or its snippet.

All successful responses will have the following structure:

```
{
  "status": "ok",
  "data": {
    "items": [
      {
        "key": "key1",
        "id": "products/item-1",
        "name": "Item 1",
        "price": 10,
        "quantity": 2,
        "totalAmount": 20,
        "options": null,
        "thumbnail": null
      },
      {
        "key": "key2",
        "id": "products/item-2",
        "name": "Item 2",
        "price": 10,
        "quantity": 1,
        "subtotal": 10,
        "options": {
          "name": "value"
        },
        "thumbnail": "https://path.com/to/image.jpg"
      }
    ],
    "totalAmount": 30,
    "totalQuantity": 3,
    "currency": "EUR",
    "currencySymbol": "€"
  },
  "snippet": null
}
```

In case of error:

```
{
  "status": "error",
  "message": "Product does not exist."
}
```

### `GET /api/cart`

[](#get-apicart)

Get cart contents.

```
const response = await fetch('api/cart', {
  method: "GET"
});
```

### `POST /api/cart/items`

[](#post-apicartitems)

Adds item to the cart.

```
const response = await fetch('/api/cart/items', {
  method: 'POST',
  body: JSON.stringify({
    id: 'products/item',
    quantity: 1,
    // optional
    options: {
      'Size': 'Medium'
    }
  })
});
```

### `PATCH /api/cart/items/:key`

[](#patch-apicartitemskey)

Updates item in the cart.

```
const key = 'key-hash';
const response = await fetch(`/api/cart/items/${key}`, {
  method: 'PATCH',
  body: JSON.stringify({
    quantity: 1
  })
});
```

### `DELETE /api/cart/items/:key`

[](#delete-apicartitemskey)

Removes item from the cart.

```
const key = 'key-hash';
const response = await fetch(`/api/cart/items/${key}`, {
  method: 'DELETE'
});
```

### `GET /api/cart/snippet`

[](#get-apicartsnippet)

Get cart snippet.

```
const response = await fetch('/api/cart/snippet', {
  method: 'GET'
});
```

Response:

```
{
  "status": "ok",
  "snippet": "  "
}
```

Translations
------------

[](#translations-1)

Currently, this plugin is only available in English and Portuguese (Portugal).

If you want to add a new translation, check the [`translations`](#translations) option in the [`Options`](#options) section.

If you want to contribute with a translation (to be bundled with the plugin), go to the `translations` directory and create a new YAML file named with the locale that you wish to translate. For example, if you want to add the German translation, create a `de.yml` file.

It will be very appreciated if you can contribute by making a pull request with the translation you wish to add.

Setup
-----

[](#setup)

Below are the steps required to set up a Stripe Checkout online shop, both in `hosted` and `embedded` mode.

Tip

It is recommended that you use a library that enables environment variables to store your project credentials in a separate place from your code and to have separate development and production access keys.

Considering that you already have a Stripe account:

### Step 1.

[](#step-1)

Grab your public and secret keys from the Stripe Dashboard and add them to the [`stripePublicKey`](#stripepublickey) and [`stripeSecretKey`](#stripesecretkey) options.

Important

Make sure to grab the test keys when in development mode, and only use the production keys when the website is live.

### Step 2.

[](#step-2)

Create a webhook to listen to Stripe Checkout events.

When creating a webhook in the Stripe Dashboard (should be in the Developers page), make sure to select the following Checkout events; otherwise it will not work correctly:

- `checkout.session.completed`
- `checkout.session.async_payment_succeeded`
- `checkout.session.async_payment_failed`

The endpoint URL must be set to the following: `https://yourdomain.com/stripe/checkout/webhook`. This is, your base URL followed by `/stripe/checkout/webhook`.

When the webhook is created, grab its secret key and add it to the [`stripeWebhookSecret`](#stripewebhooksecret) option.

Important

The webhook will not work properly when developing locally, since the request cannot reach a local endpoint that only exists on your computer. Check the [Development](#development) section for more information on how to work with webhooks in development.

### Step 3.

[](#step-3)

For the panel, you need to create a `orders` and a `order` blueprint. You can change the `orders` name with the [`ordersPage`](#orderspage) option.

```
# blueprints/pages/orders.yml

extends: stripe-checkout.pages/orders
```

```
# blueprints/pages/order.yml

extends: stripe-checkout.pages/order
```

Note

Remember to create a `orders` directory at `/content` with a `orders.txt` file. Otherwise, the page will not be found.

### Step 4 (optional).

[](#step-4-optional)

Similar to the previous step, create a `checkout-settings` blueprint. You can change the `checkout-settings` name with the [`settingsPage`](#settingspage) option.

Currently, the only existing settings are to manage shipping data, like allowed countries and shipping rates. If you don't need this information for your website (for example, if you are selling digital assets, where shipping information is not needed), you can skip this step.

```
# blueprints/pages/checkout-settings.yml

extends: stripe-checkout.pages/checkout-settings
```

### Step 5.

[](#step-5)

You can create a product blueprint with any name.

Make sure that you have a `price` field (it is required). To add an image, add a `thumbnail` field (it is optional).

The plugin already comes with both blueprints fields, in case you want to use them:

```
# blueprints/pages/product.yml

title: Product

fields:
  price: stripe-checkout.fields/price
  thumbnail: stripe-checkout.fields/thumbnail # optional
```

### `hosted` versus `embedded` mode

[](#hosted-versus-embedded-mode)

Depending on the mode you are using, jump to the respective step below:

- [Step 6: `hosted` mode](#step-6-hosted-mode)
- [Step 6: `embedded` mode](#step-6-embedded-mode)

For more information about the difference between both modes, check the [`uiMode`](#uimode) option.

### Step 6: `hosted` mode.

[](#step-6-hosted-mode)

When in `hosted` mode, you need to add a link to the website with the URL generated by the following method `stripeCheckout()->checkoutUrl()`.

This link usually exists in the cart component or when reviewing the order before proceeding to the checkout.

Something like:

```

  Continue shopping
