# PrestaShop

> A payment module for PrestaShop: buyers pay in bitcoin or USDC/USDT straight to your wallet, and a signed webhook makes the order.

The Perly Pay module adds **Pay with crypto** to your checkout. The cart becomes an invoice in Perly Pay; the buyer pays it on its own page or over your shop; the order is made once money is on its way. PrestaShop 8.0 to 9.x, PHP 8.1+ with curl. Nothing to install with Composer: the [PHP SDK](https://pay.perly.io/docs/sdks.md) is inside.

> **Note:** Perly Pay bills in USD or EUR. A cart in another currency does not see the payment option, and the settings page names those currencies. Payment → Preferences narrows the currencies, countries and groups as for any payment module.

## Install

1. **Get the module** — Download `perlypay.zip` from the latest release on [GitHub](https://github.com/volodymyrslipak/perly-pay-prestashop/releases/latest).
2. **Upload** — In the back office: Modules → Module Manager → **Upload a module**, and drop `perlypay.zip`.
3. **Configure** — Press **Configure** on Perly Pay.

## Settings

| Setting | What to put |
| --- | --- |
| Secret key | From Dashboard → Developers → API keys. `sk_test_…` for [test mode](https://pay.perly.io/docs/test-mode.md), `sk_live_…` for real money. Full access, or Restricted with **Webhook endpoints** ticked. |
| Webhook signing secret | `whsec_…`. **Set up webhook** fills it in for you. |
| Payment page | **Redirect** (the default) sends the buyer to the payment page on pay.perly.io. **Overlay** opens it over the shop. Phones always get the page itself. |
| Open refunds in Perly Pay | On by default: a credit slip on an order Perly Pay paid opens a refund there. |
| Debug log | Ids and outcomes to Advanced Parameters → Logs. Never keys, secrets or customers' details. |

1. **Paste the key** — Save, then press **Test connection**: it says whether the key works, its mode, and whether this shop's webhook is in Perly Pay.
2. **Set up the webhook** — Press **Set up webhook**. It adds the shop's address, `https://your-shop/module/perlypay/webhook`, in Perly Pay with the invoice and refund events and saves its secret. Or add it yourself in Developers → Webhooks and paste the secret.
3. **Allow the overlay** — For **Overlay**, add the shop's address in Dashboard → Settings → Checkout → **Sites that may embed the checkout**.

## What buyers pay with

The buyer picks a coin and network on the payment page and pays from any wallet. The money goes straight to yours.

| Coin | Networks |
| --- | --- |
| BTC | Bitcoin |
| USDC | Base, Arbitrum, Optimism and Ethereum |
| USDT0 | Arbitrum and Optimism |
| USDT | Ethereum and TRON |

> **Note:** The module is free and open source (MIT). Perly Pay costs 2.5¢ a payment, never more than 0.5% of it, billed after the month. A month of up to 21 payments costs $0. Nothing is taken from the payment: see [Pricing](https://pay.perly.io/pricing).

## Test

With a `sk_test_…` key, buy something with **Pay with crypto**. Pay with test coins, or from the API — the invoice id `inv_…` is in the payment page's address:

```bash
curl -X POST https://pay.perly.io/api/v1/invoices/inv_…/simulate \
  -H "Authorization: Bearer $PERLY_PAY_SECRET_KEY" -H "Content-Type: application/json" -d '{}'
```

Within seconds the order is **Payment accepted** and the customer has the confirmation email.

## How orders follow payments

No order is made when the buyer leaves for the payment page, so abandoned crypto checkouts do not fill the back office. The first webhook that says money is on its way makes the order, for the amount Perly Pay billed.

| In Perly Pay | The order |
| --- | --- |
| Seen, not yet confirmed | **Awaiting crypto confirmation**, one of the module's two order states. |
| Paid and the sender checked (`invoice.fulfilled`) | **Payment accepted**. The invoice id is the payment's transaction id, the customer gets PrestaShop's emails, and a private message names the network, the coin amount and the transaction. |
| Held for [review](https://pay.perly.io/docs/screening.md) | **Crypto payment held**. Decide in Perly Pay. |
| Part paid, overpaid or paid late | A private message on the order. |
| Expired with part paid | Held. |
| Expired, nothing paid | No order. The cart stays as it was. |
| A seen payment that never arrived | **Payment error**. |

A webhook only names the invoice: the module asks Perly Pay's API for it with the shop's own key, and that answer decides the order. Each event is applied once, to the cart it was made for. When the buyer comes back before the webhook, the module asks the API and applies the same rules.

## Refunds

Give a credit slip as usual (Partial refund, Standard refund, Return products): the module opens a [refund](https://pay.perly.io/docs/refunds.md) in Perly Pay for the slip's amount, in the coin that was paid. Perly Pay emails the customer for an address; you send from your wallet and paste the transaction id in Perly Pay. The order's messages say each step. A slip given as a voucher opens no refund. A full refund made in Perly Pay marks the order **Refunded**.

## Go live

- Turn on live mode in Perly Pay and connect your wallet's public keys.
- Paste a `sk_live_…` key and press **Set up webhook** again: a live key makes a live webhook.
- For **Overlay**, add the shop's https address to **Sites that may embed the checkout** in live mode too: test and live keep separate lists.
- Perly Pay sends webhooks only to `https://` addresses.
- Sell one small order for real and refund it with a credit slip.
