# PayPal Reader (Zettle) Gateway

The till sends the order total to a PayPal Reader, the customer taps or inserts their card on the reader, and the sale finishes on its own when the reader reports the payment complete. This page is in two parts: [taking a payment](#take-payment) at the till, and [setting it up](#setup) in WP Admin.

Pro Feature

The PayPal Reader extension needs [WCPOS Pro](/ar/getting-started/pro-license.md), both to install it from the extension directory and to use it at the till.

<!-- -->

## Take a payment[​](#take-payment "رابط مباشر إلى Take a payment")

Before you start

* The till and your store are online. The payment runs on your store's own payment page, inside the POS.
* Someone with WP Admin access has [set the gateway up and paired a reader](#setup).
* The register is open, if your store uses **Open and close the register**. The till won't record a payment while it's closed. See [Registers](/ar/manage-store/stores/register-settings.md#one-register).

1. Build the cart and press **Checkout**.

2. Open the **Legacy** tab. Your store's payment page opens inside the POS. See [The Legacy Tab](/ar/sell/take-payment/legacy-tab.md).

3. Choose **PayPal Reader**. Your store may have given it another name. The panel says **Loading readers…**, then **Mode: test. Ready to start payment.** (or **Mode: live.** once the store has [gone live](#going-live)).

4. Pick the reader. Every paired reader is a button with its name, such as **Front counter (ready)**, and the first one is already chosen. Tap another to use it instead.

   تحذير

   **TEST MODE** at the top of the panel is only a label. The reader can still charge real money. See [Test mode and going live](#going-live).

5. Press **Start reader payment**. The panel says **Connecting to reader…**, then **Reader ready. Requesting payment…**, and the full order total goes to that reader.

6. The customer taps or inserts their card on the reader. The panel says **Payment in progress…**, with the reader's current step in brackets, while the reader works.

7. When the reader reports the payment complete, the panel says **Payment complete. Submitting order…** and the POS moves on to the receipt.

You don't need **Process Payment** at the foot of the Legacy tab: the sale finishes on its own. Press it before the reader finishes and the POS says **Complete the PayPal Reader payment before placing the order.**

The dark box under the buttons keeps a timed line for every step. Read it when a payment stops partway.

The reader is always charged the whole order, so it can't share a sale with another method. Once a payment has been taken on the order, the **Legacy** tab reads **Whole bill only** and can't be used; [cancel that payment](/ar/sell/take-payment/cancel-payment.md) first.

### Stop a payment[​](#cancel "رابط مباشر إلى Stop a payment")

Press **Cancel payment** while the payment runs. It asks the reader to cancel and stops the till listening to it, and the panel says **Payment canceled.**

Check the reader before you charge again

After **Cancel payment**, or if the payment page is closed or reloaded while the reader is working, the till no longer hears from the reader. If the reader shows the payment approved anyway, the customer has paid: don't charge them again. Record it with the **Card** tile on the **Payments** tab, and check the payment in your Zettle account.

Once you're sure the customer wasn't charged, press **Start reader payment** to try again, or close the tab and take another method.

## After the sale[​](#after-the-sale "رابط مباشر إلى After the sale")

The order is marked paid, and its **transaction ID** is Zettle's ID for the card payment, so you can find the same payment in your Zettle account. Order notes record when the payment started and on which reader (by its Link ID), and when it completed, was refused for an amount mismatch, or was cancelled with **Cancel payment**.

## Refunds[​](#refunds "رابط مباشر إلى Refunds")

The extension can't refund through Zettle, so **Refund to PayPal Reader** isn't available.

تحذير

Don't hand the customer any cash. The card refund is made in Zettle, and **Refund via cash** only records it in WCPOS. If the register is open, it also lowers the cash the register expects in the drawer by the same amount. See [Cash refunds and the drawer](/ar/sell/refunds.md#drawer).

1. Refund the card in your Zettle account.
2. Record the refund on the order with **Refund via cash**. See [Refund Destination](/ar/sell/refunds.md#destination).

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

**No readers paired yet. Ask the store admin to pair one…** The Zettle account has no reader paired, so **Start reader payment** stays greyed out. Someone with WP Admin access has to pair one: see **Pair a reader** under [Set it up](#setup).

**The panel stays on Connecting to reader…** The reader isn't ready for a payment: it may be asleep, off, offline or busy with something else. Wake it and check it is on its home screen and online. Press **Cancel payment**, check the reader shows no payment taken, as in [Stop a payment](#cancel), then press **Start reader payment** again.

**Payment canceled.** The payment was cancelled on the reader or with **Cancel payment**. If you pressed **Cancel payment**, check the reader first, as in [Stop a payment](#cancel). Then start it again, or take another method.

### Payment failed. Was the card charged?[​](#payment-failed "رابط مباشر إلى Payment failed. Was the card charged?")

What comes after **Payment failed.** tells you whether the customer has paid, and so whether you may charge again. Find the message, then follow its case:

* **Payment amount mismatch.**: [charged, refund first](#failed-mismatch).
* A reason in brackets: [not charged, try again](#failed-reason).
* **Request failed.**, **Invalid server response.**, **Invalid request.** or **Invalid order access.**: [charged, record the payment](#failed-not-recorded).
* Nothing after it, or a message not listed here: [check before you try again](#failed-connection).

#### Payment amount mismatch: charged, refund first[​](#failed-mismatch "رابط مباشر إلى Payment amount mismatch: charged, refund first")

**Card charged: yes.** The panel says **Payment failed. Payment amount mismatch.** The reader took the money, but the order's total changed after the payment started, so the order is refused and stays unpaid. Refund that payment in your Zettle account, then start a new payment for the right total. To avoid it, don't change the cart while a reader payment runs: press **Cancel payment**, change the cart, then start a new payment.

#### A reason in brackets: not charged, try again[​](#failed-reason "رابط مباشر إلى A reason in brackets: not charged, try again")

**Card charged: no.** The panel says **Payment failed.** with a reason in brackets. The reader or Zettle didn't take the payment, for example the card was declined. Press **Start reader payment**, or take another method.

#### Only Payment failed. or an unlisted message: check before you try again[​](#failed-connection "رابط مباشر إلى Only Payment failed. or an unlisted message: check before you try again")

**Card charged: can't tell.** The panel says only **Payment failed.** The till lost its connection to Zettle and can't tell whether the card was charged. Treat a message this page doesn't list the same way. Check the reader and the payment list in your Zettle account. If the payment is there, record it as in [Stop a payment](#cancel). Start again only once you're sure it wasn't taken; if you can't tell, ask whoever manages your Zettle account before you charge again.

#### Request failed. or another message listed here: charged, record the payment[​](#failed-not-recorded "رابط مباشر إلى Request failed. or another message listed here: charged, record the payment")

This case covers only these messages after **Payment failed.**: **Request failed.**, **Invalid server response.**, **Invalid request.** and **Invalid order access.** For any other message, follow [Only Payment failed.](#failed-connection) instead.

**Card charged: yes.** The panel only shows these messages after the reader has reported the payment complete: the reader took the payment, but the store couldn't record it, so the order stays unpaid. Find the payment in your Zettle account and record it as in [Stop a payment](#cancel). If it isn't in your Zettle account, follow [Only Payment failed.](#failed-connection) and don't charge again until you're sure.

### Other messages[​](#other-messages "رابط مباشر إلى Other messages")

**Zettle credentials are not configured.** The **Zettle Client ID** or **Zettle Assertion** is empty. Ask whoever set up the gateway to fill them in: see **Fill in the gateway settings** under [Set it up](#setup).

**A message starting Zettle OAuth exchange returned HTTP …** Zettle refused the credentials. Ask whoever set up the gateway to check the **Zettle Client ID** and **Zettle Assertion**.

**The panel shows its buttons but never says Loading readers…** The payment page's scripts aren't loading. See [A Gateway's Payment Page Looks Wrong](/ar/troubleshooting/common-problems/checkout-problems.md#checkout-settings-troubleshooting).

Messages while setting up

**Save your Zettle Client ID and Assertion above before pairing a reader.** The **Paired readers** section waits for both credentials. Enter them and save.

**Enter the pairing code first.** The **Pairing code** box is empty. Read the code off the reader and type it in.

**Could not load paired readers:** followed by Zettle's answer. The settings page couldn't reach Zettle, or Zettle refused the credentials. Check them and save again.

**Pair reader shows a message starting Zettle API returned HTTP …** Zettle refused the pairing code. On the reader, open **Settings → Link with a developer** for a fresh code, type it exactly as shown, and check the reader is online. If it still fails, check the **Zettle Client ID** and **Zettle Assertion** are saved.

**PayPal Reader: mock reader is active.** A developer setting, `PRWC_USE_MOCK_READER`, is defined in `wp-config.php`. No real payments are taken while it is set. Ask your developer or host to remove it.

## Set it up[​](#setup "رابط مباشر إلى Set it up")

This part is for whoever runs the store's WP Admin: five steps, from installing the extension to switching it on for the POS.

Install, enter credentials, pair a reader and enable it for the POS

1

### Install the extension

Open `WP Admin > POS > Settings > Extensions`, find **PayPal Reader for WooCommerce**, click **Install**, then **Activate**. See [Installing an Extension](/ar/manage-store/add-ons.md#installing-an-extension).

Without the directory, download the **plugin zip** (not the source-code zip) from the [GitHub releases page](https://github.com/wcpos/paypal-reader-for-woocommerce/releases) and upload it with `Plugins > Add Plugin`, then **Upload Plugin**. WooCommerce must be active.

2

### Get your Zettle credentials

In the Zettle Developer Portal, get a **Client ID** and an **Assertion** (a JWT) for your Zettle account. To try the gateway without taking real money, use a Zettle developer (test) merchant account first.

Which PayPal Reader models and countries you can use is up to your Zettle account, not WCPOS. Check with PayPal before buying a reader.

3

### Fill in the gateway settings

1. Go to `WP Admin > WooCommerce > Settings > Payments` and open **PayPal Reader**.
2. Leave **Enable Test mode** ticked, the default, while you try it with your test account.
3. Enter the **Zettle Client ID** and the **Zettle Assertion**.
4. Optional: change the **Title** cashiers choose on the payment page, and its **Description**.
5. Save.

4

### Pair a reader

1. Scroll to **Paired readers** at the foot of the same settings page. It appears once both credentials are saved.
2. On the PayPal Reader, open **Settings → Link with a developer**. The reader shows a pairing code.
3. Under **Pair a new reader**, enter the **Pairing code**, and a **Reader name** such as `Front counter`. Without a name, it is called `WCPOS PayPal Reader`.
4. Click **Pair reader**. The page says **Reader paired.** and the reader joins the list.

Every till sees every reader paired to the Zettle account, and the cashier picks one at checkout. To remove one, click **Unpair** beside it and confirm.

5

### Turn it on for the POS

Go to `WP Admin > POS > Settings > Checkout`, find **PayPal Reader** and switch **Enabled** on. See [Enabling a Gateway](/ar/settings/checkout.md#enabling-a-gateway).

ملاحظة

Leave **Enable PayPal Reader for web checkout (not necessary for WCPOS)** unticked. It only lists the gateway on the online store; the POS uses it once it is enabled here.

## Test mode and going live[​](#going-live "رابط مباشر إلى Test mode and going live")

**Test mode** doesn't switch the reader to a sandbox. Zettle has one set of addresses for test and real payments, and the account behind your credentials decides whether real money moves. Test mode only changes labels on the payment panel: the **TEST MODE** badge, and **Mode: test.** in the status line.

To go live:

1. Replace the **Zettle Client ID** and **Zettle Assertion** with the ones for your real Zettle account.
2. Untick **Enable Test mode**, and save.
3. Check **Paired readers**. The list comes from the Zettle account, so readers paired to the test account aren't there: pair them again, as in **Pair a reader** under [Set it up](#setup).
4. Take a small real payment and refund it in Zettle.

## Settings reference[​](#settings "رابط مباشر إلى Settings reference")

On `WP Admin > WooCommerce > Settings > Payments > PayPal Reader`. The steps above cover the ones you need; open the list for every setting and its default.

All 6 settings, and the pairing panel

| Setting              | What it does                                                                                                                                                                                                                                                       |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Enable/Disable**   | **Enable PayPal Reader for web checkout (not necessary for WCPOS)**. Lists the gateway on the online store. Leave it off: online, it only works on an existing order's payment page. Off by default. The POS has its own switch, under `POS > Settings > Checkout` |
| **Title**            | The name cashiers choose on the payment page. Defaults to `PayPal Reader`                                                                                                                                                                                          |
| **Description**      | The line shown under the title. Defaults to `Pay in person using PayPal Reader.`                                                                                                                                                                                   |
| **Test mode**        | **Enable Test mode**. Shows the **TEST MODE** badge on the payment panel. On by default. It doesn't change where payments go                                                                                                                                       |
| **Zettle Client ID** | Your Zettle OAuth client ID, from the Zettle Developer Portal                                                                                                                                                                                                      |
| **Zettle Assertion** | Your Zettle OAuth assertion (a JWT). Kept hidden like a password                                                                                                                                                                                                   |

**Paired readers**, below the settings, lists each reader by name and **Link ID**, with **Unpair**. **Pair a new reader** takes a **Pairing code** and a **Reader name**.

## Logs[​](#logs "رابط مباشر إلى Logs")

Order notes are the first place to look: a payment's start, its completion or amount refusal, and a **Cancel payment** pressed at the till are noted on its order. A decline, a cancel on the reader or a lost connection isn't noted: read the log box on the payment page.

When Zettle's reader service refuses a request or can't be reached, the extension also writes it to `WP Admin > WooCommerce > Status > Logs`, under the source `paypal-reader-for-woocommerce`. Send that log and the **order number** when you ask for help.

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

Zettle Account

<!-- -->

: A PayPal Zettle merchant account, with a Client ID and an Assertion (JWT) from the Zettle Developer Portal

Reader

<!-- -->

: A PayPal Reader, paired to that account. Models and countries depend on your Zettle account

Connection

<!-- -->

: Online only. The till talks to the reader through Zettle over the internet, so the till, the reader and your store all need a connection

WCPOS

<!-- -->

: WCPOS Pro

Server

<!-- -->

: WordPress 5.2 or newer, WooCommerce, and PHP 7.4 or newer

## What it doesn't do[​](#limitations "رابط مباشر إلى What it doesn't do")

* Refunds through Zettle, from WooCommerce or the POS.
* Tips on the reader.
* Part-payments: the reader is always charged the whole order.
* Picking up a payment after the payment page closes or reloads.
* The online store's normal checkout. Online, it only works on an existing order's payment page.

How it works, for developers

The store signs in to Zettle with the JWT-bearer OAuth grant (`oauth.zettle.com`) and caches the access token until 30 seconds before it expires. Pairing, the reader list and payment sessions use Zettle's **Reader Connect** API (`reader-connect.zettle.com/v1/integrator`): pairing claims a link offer with the reader's code, and each reader is a link.

**Start reader payment** asks the store to open a session for the chosen link. The payment page then opens a WebSocket to Zettle, asks for the reader's state, and sends the payment request for the order total, with no tipping, once the reader is ready. Progress comes back over the same socket. **Cancel payment** sends a cancel request on it.

When the reader reports `COMPLETED`, the page sends the result to the store, which accepts it only if the amount matches the order total. It saves Zettle's card payment ID as the transaction ID and places the order. The order can't be placed until that check has passed.

For automated tests, `PRWC_USE_MOCK_READER` (or the `paypal_reader_for_woocommerce_use_mock_reader` filter), with `PRWC_MOCK_CANCEL_BEHAVIOR`, swaps Zettle for a mock reader. It is never shown in the settings, and WP Admin warns while it is on.

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

* [The Legacy Tab](/ar/sell/take-payment/legacy-tab.md)
* [Checkout](/ar/getting-started/make-sale.md)
* [Checkout Settings](/ar/settings/checkout.md)
* [Extensions](/ar/manage-store/add-ons.md)
* [Report an issue on GitHub](https://github.com/wcpos/paypal-reader-for-woocommerce/issues). For your Zettle account, readers and countries, contact PayPal.

[The Legacy TabGateways that take payment on the store's own payment page](/ar/sell/take-payment/legacy-tab.md)

[Checkout SettingsTurn gateways on for the POS](/ar/settings/checkout.md)

[All GatewaysThe other card readers WCPOS works with](/ar/settings/checkout/payment-gateways.md)
