# Gateway Template

* **Taking a customer's payment?** See [Taking payment on the Legacy tab](/ar/sell/take-payment/legacy-tab.md).
* **Gateway already set up?** [Try a test payment at the till](#at-the-till).
* **Building a gateway?** This page is for developers. It shows how to [build your own POS payment gateway](#build), install it and turn it on.

Pro Feature

Gateways other than Cash and Card need [WCPOS Pro](/ar/getting-started/pro-license.md). Without it, your gateway is listed in the POS checkout settings but can't be turned on.

The gateway template is a starting point for your own payment method at the till: a WordPress plugin made of one PHP file. A setup script fills in your gateway's name and builds the plugin for you. The gateway stays off on your web store's checkout, and appears in the POS once you turn it on in the POS settings.

At the till, your gateway is on the checkout's [Legacy tab](/ar/sell/take-payment/legacy-tab.md). The tab opens your gateway's WooCommerce payment page inside the POS. The cashier picks the gateway there and presses **Process Payment** at the foot of the tab.

## Try a payment at the till[​](#at-the-till "رابط مباشر إلى Try a payment at the till")

Once your gateway is [built and turned on](#build), a payment with it goes like this. A developer builds and installs the gateway; the cashier takes the payment.

The Legacy tab needs your store to be reachable, and it charges the whole order, so it can't be used once any payment has been taken on the order. See [The Legacy Tab](/ar/sell/take-payment/legacy-tab.md).

Use a test order

The unchanged template doesn't collect any money. It only marks the order paid, so try it on a test order, not a real customer's sale. Your own payment logic goes in `process_payment()`.

1. If your store uses register sessions, open the register. Add a product to the cart and press **Checkout**.
2. Open the **Legacy** tab, next to **Payments** at the top of the payment screen.
3. Pick your gateway on the payment page, then press **Process Payment** at the foot of the tab.
4. The sale completes and the receipt is shown, as on the [paid screen](/ar/getting-started/make-sale.md#paid), with the order at the gateway's **Order Status**. A [pay-later gateway](#pay-later), set up as below, closes the sale too, but the order stays at **Pending payment**.

For developers: how the POS knows the payment is done

**Process Payment** sends the payment page, and WooCommerce runs your gateway's `process_payment()`. When that returns the order-received address, `get_return_url( $order )`, the POS checks the order's status:

* **Paid (Processing or Completed), or the gateway's Order Status** from the POS checkout settings — the POS closes the sale.
* **Anything else** — the POS keeps waiting, and the sale stays open.

## What you need[​](#requirements "رابط مباشر إلى What you need")

WCPOS Pro

<!-- -->

: To turn the gateway on in the POS

WooCommerce

<!-- -->

: Installed and active

Bash

<!-- -->

: To run the setup script

PHP

<!-- -->

: Some PHP, to customise the payment logic

## Build your gateway[​](#build "رابط مباشر إلى Build your gateway")

### Get the template[​](#get-template "رابط مباشر إلى Get the template")

```
git clone https://github.com/wcpos/woocommerce-gateway-template.git

cd woocommerce-gateway-template
```

Or press **Use this template** on the [GitHub repository](https://github.com/wcpos/woocommerce-gateway-template) to make your own copy, then clone that.

### Run the setup script[​](#setup-script "رابط مباشر إلى Run the setup script")

```
./create-gateway.sh
```

It's a Bash script. It asks for:

* **Gateway name** — the only answer it needs, for example `Cash Payment`.
* **Gateway slug** — press Enter to use the one made from the name (`cash-payment`). Lowercase letters, numbers and hyphens only.
* **Gateway description** and **default checkout description** — optional.
* **GitHub username**, **repository name** and **author name** — optional. The repository name defaults to the slug plus `-gateway`.
* **Target directory** — where to put the plugin. It defaults to the folder that holds the template.

The plugin lands in a folder named after the repository, for example `cash-payment-gateway/`, with `wcpos-cash-payment.php` inside.

### Install the plugin[​](#install "رابط مباشر إلى Install the plugin")

Either copy the generated folder into your site's `wp-content/plugins/` directory, or zip the folder, then in WordPress admin go to **Plugins > Add Plugin**, press **Upload Plugin**, choose the zip and press **Install Now**.

Then activate it. WooCommerce has to be active too: without it, the plugin loads nothing.

### Turn it on for the POS[​](#enable "رابط مباشر إلى Turn it on for the POS")

1. Go to **WP Admin > POS > Settings > Checkout**.
2. Find your gateway in the **Gateways** list, under the name you gave it. Its **Gateway ID** is `wcpos_` plus your slug with underscores, for example `wcpos_cash_payment`.
3. Switch **Enabled** on, and pick the **Order Status** a sale paid this way should end on. See [Checkout Settings](/ar/settings/checkout.md#payment-gateways).

The gateway shows on the POS's Legacy tab the next time the POS loads. To test it, [try a payment at the till](#at-the-till).

## What's in the plugin file[​](#how-it-works "رابط مباشر إلى What's in the plugin file")

The plugin registers one WooCommerce payment gateway class. It stays off your web store's checkout, marks the order paid (for a POS sale, at the gateway's **Order Status**), and sends the POS to the order-received page.

The lines that matter

* **`$this->enabled = 'no';`** in the constructor — keeps the gateway off your web store's checkout, even if it's switched on in WooCommerce's payment settings. WCPOS switches it on only for POS requests, and only when it's enabled in the POS checkout settings.
* **`process_payment()`** — calls `$order->payment_complete()`, which marks the order paid and reduces stock. For a POS order, WCPOS sets the status to the gateway's **Order Status** from the POS checkout settings (**Completed** unless you chose another). It returns `get_return_url( $order )`, the order-received address the POS waits for.
* **`init_form_fields()`** — the **Title** and **Description** fields in WooCommerce's payment settings. The POS can give the gateway its own title from **POS settings** in the gateway's menu.

The plugin also loads its own text domain, `woocommerce-pos-<your-slug>-gateway`, with a `.pot` file in `languages/`.

## Customise your gateway[​](#customise "رابط مباشر إلى Customise your gateway")

### Ask the cashier for something[​](#payment-fields "رابط مباشر إلى Ask the cashier for something")

If your gateway needs input from the cashier, such as a reference number, set `$this->has_fields = true;` in the constructor and add a `payment_fields()` method. Its fields show on the gateway's payment page in the Legacy tab. Read the value in `process_payment()` from `$_POST['my_gateway_reference']`, cleaned with `sanitize_text_field( wp_unslash( ... ) )`.

Show the payment\_fields() code

```
public function payment_fields() {

    if ( $this->description ) {

        echo wpautop( wptexturize( $this->description ) );

    }



    woocommerce_form_field( 'my_gateway_reference', array(

        'type'     => 'text',

        'label'    => __( 'Reference Number', 'your-text-domain' ),

        'required' => true,

    ) );

}
```

### Let the customer pay later[​](#pay-later "رابط مباشر إلى Let the customer pay later")

The template marks the order paid straight away. For a gateway where the money arrives later, such as an invoice, set the order to **Pending payment** in `process_payment()` and reduce stock yourself, since `payment_complete()` isn't called.

Show the process\_payment() code

```
public function process_payment( $order_id ) {

    $order = wc_get_order( $order_id );



    if ( ! $order ) {

        return array( 'result' => 'failure' );

    }



    // The customer pays later.

    $order->update_status( 'pending', __( 'Awaiting payment', 'your-text-domain' ) );



    // payment_complete() is not called, so reduce stock here.

    wc_reduce_stock_levels( $order_id );



    return array(

        'result'   => 'success',

        'redirect' => $this->get_return_url( $order ),

    );

}
```

Then set the gateway's **Order Status** to **Pending payment** in **WP Admin > POS > Settings > Checkout**. **Pending payment** isn't a paid status, so without that setting the POS keeps waiting and the sale stays open. See [How the till treats the status](/ar/settings/checkout.md#order-status-at-the-till).

Fill in the placeholders by hand

If you'd rather not run the script, use **Use this template** on GitHub, then find and replace these placeholders in every file:

| Placeholder                       | What to enter                                            | Example                            |
| --------------------------------- | -------------------------------------------------------- | ---------------------------------- |
| `{{GATEWAY_NAME}}`                | Your gateway's name                                      | Cash Payment                       |
| `{{GATEWAY_SLUG}}`                | Lowercase, with hyphens                                  | cash-payment                       |
| `{{GATEWAY_DESCRIPTION}}`         | A short description of the gateway                       | Accept cash payments in WCPOS      |
| `{{GATEWAY_DEFAULT_DESCRIPTION}}` | The description shown on the payment page                | Pay with cash at the point of sale |
| `{{GITHUB_USERNAME}}`             | Your GitHub username                                     | kilbot                             |
| `{{REPO_NAME}}`                   | Your repository name                                     | cash-payment-gateway               |
| `{{AUTHOR_NAME}}`                 | Your name                                                | kilbot                             |
| `{{GATEWAY_ID}}`                  | The slug, with underscores instead of hyphens            | cash\_payment                      |
| `{{GATEWAY_CLASS_NAME}}`          | Each word of the name capitalised, joined by underscores | Cash\_Payment                      |
| `{{GATEWAY_FUNCTION_PREFIX}}`     | The same as the gateway ID                               | cash\_payment                      |

Then rename `wcpos-{{GATEWAY_SLUG}}.php` to match your slug, for example `wcpos-cash-payment.php`. The script works out the last three values from the name and slug for you.

## Release an update[​](#updates "رابط مباشر إلى Release an update")

Edit `wcpos-<your-slug>.php`, raise the `Version:` number in the plugin header (for example `0.1.0` to `0.2.0`), then install it again: replace the folder in `wp-content/plugins/`, or upload a new zip. WordPress asks before it replaces the installed plugin; choose **Replace current with uploaded**.

### Automatic releases on GitHub[​](#github-releases "رابط مباشر إلى Automatic releases on GitHub")

The generated plugin includes two GitHub Actions workflows. If you push it to a GitHub repository:

1. Raise the `Version:` number in the plugin header.
2. Commit and push to `main`.
3. The release workflow sees a version with no `v<version>` tag yet and publishes a release with `woocommerce-pos-<your-slug>-gateway.zip` attached.

Anyone can then download that zip from the repository's **Releases** page and install it like any WordPress plugin. The second workflow regenerates the `.pot` translation file whenever a PHP file changes on `main`.

## If something goes wrong[​](#troubleshooting "رابط مباشر إلى If something goes wrong")

The gateway isn't in the POS checkout settings

In **WP Admin > Plugins**, check that your gateway plugin is activated and that WooCommerce is active.

The gateway is listed but its Enabled switch is greyed out

The free plugin only turns on **Cash** and **Card**. Activate [WCPOS Pro](/ar/getting-started/pro-license.md).

It isn't on the Legacy tab

At the till: if a payment has already been taken on the order, the Legacy tab can't be used until that payment is [cancelled](/ar/sell/take-payment/cancel-payment.md).

In WP Admin: switch **Enabled** on in **WP Admin > POS > Settings > Checkout**, then reload the POS.

The sale doesn't close after payment

The order isn't paid and isn't at the gateway's **Order Status**.

In WP Admin: set the gateway's **Order Status** in **WP Admin > POS > Settings > Checkout** to the status your `process_payment()` sets.

In the code: make sure `process_payment()` returns `get_return_url( $order )`.

The order ends on a status you didn't expect

For POS sales, the gateway's **Order Status** in the POS checkout settings decides it, not the products in the order. See [Order Status](/ar/settings/checkout.md#order-status).

It shows on the web store's checkout

In the code: check that `$this->enabled = 'no';` is still in the constructor, after `init_settings()`.

## Examples and resources[​](#resources "رابط مباشر إلى Examples and resources")

Two WCPOS gateways are built the same way, one PHP file each, and are good to read next to the template:

* **[Email Invoice](/ar/sell/take-payment/email-invoice.md)** — sets the order to **Pending payment**, reduces stock and emails the customer a link to pay ([source](https://github.com/wcpos/email-invoice-gateway)).
* **[Web Checkout](/ar/sell/take-payment/web-checkout.md)** — sends the customer to your web store's checkout to pay ([source](https://github.com/wcpos/web-checkout-gateway)).

The template is on [GitHub](https://github.com/wcpos/woocommerce-gateway-template). For everything a gateway class can do, see WooCommerce's [WC\_Payment\_Gateway reference](https://woocommerce.github.io/code-reference/classes/WC-Payment-Gateway.html).

## Related Documentation[​](#related-documentation "رابط مباشر إلى Related Documentation")

[The Legacy TabWhere your gateway's payment page opens at the till](/ar/sell/take-payment/legacy-tab.md)

[Checkout SettingsTurn the gateway on and choose its order status](/ar/settings/checkout.md)

[Payment GatewaysThe gateways WCPOS already has](/ar/settings/checkout/payment-gateways.md)
