# n8n

> Crypto payments in any n8n workflow: a trigger for paid invoices, refunds and subscriptions, and a node that makes invoices and payment links.

The Perly Pay nodes bring your payments to n8n. A paid invoice can fill a sheet, email a download link or grant access; a form or a CRM step can create an invoice and send its payment page. Two nodes: **Perly Pay**, which does things, and **Perly Pay Trigger**, which starts a workflow.

> **Note:** The key decides the mode. A workflow built with a test key only ever sees test payments and can never move real money.

## Install

1. **Install the nodes** — Follow n8n's [guide to installing community nodes](https://docs.n8n.io/integrations/community-nodes/installation/) with the package name `n8n-nodes-perly-pay`. It is on [npm](https://www.npmjs.com/package/n8n-nodes-perly-pay), with its source on [GitHub](https://github.com/volodymyrslipak/n8n-nodes-perly-pay), and has no dependencies of its own.
2. **Get a key** — In Perly Pay, Dashboard → Developers → API keys. `sk_test_…` for [test mode](https://pay.perly.io/docs/test-mode.md), `sk_live_…` for real money. Choose **Full** access, or **Restricted** with **Webhook endpoints** ticked: the trigger adds its own webhook.
3. **Add the credential** — In n8n, add a **Perly Pay API** credential and paste the key. n8n tests it by listing your webhook endpoints.

## Perly Pay node

| Resource | Operation | What it does |
| --- | --- | --- |
| Invoice | Create | An invoice for an amount in USD or EUR, with its payment page (`url`) where the buyer picks a coin and network. Optional: the buyer's email, a return URL, how long the buyer has to choose, your own metadata. |
| Invoice | Get | An invoice by its ID: its status, what arrived and on which network. |
| Invoice | Get Many | Invoices, newest first, by status or customer. |
| Customer | Search | Customers by email, or all of them, newest first. |
| Payment Link | Create | A shareable payment page for one of your prices, or for a new one-time price from a name and an amount. |

**Simplify**, on by default, gives an invoice or a link as ten flat fields. Amounts come as integers (cents; coins in their smallest unit), each with a decimal string beside it: `total` is `4900` and `total_decimal` is `"49.00"`.

## Perly Pay Trigger

Choose the events that start the workflow. Activating the workflow adds a webhook in your Perly Pay account for those events, at the workflow's production URL; deactivating it deletes the webhook.

| Event | When |
| --- | --- |
| Invoice Paid (`invoice.fulfilled`) | Paid in full, confirmed on its network and screened: the moment to deliver |
| Payment Underpaid (`invoice.underpaid`) | Less than the invoice arrived. The buyer can send the rest. |
| Refund Opened (`refund.requested`) | A refund was opened. Perly Pay asks the buyer for an address. |
| Refund Confirmed (`refund.confirmed`) | The refund you sent from your wallet is confirmed on chain |
| Subscription Started (`subscription.created`) | A subscription started |
| Subscription Renewed (`subscription.renewed`) | A subscriber paid for the next period |
| Subscription Ended (`subscription.ended`) | A subscription ended: the moment to take access away |
| New Customer (`customer.created`) | A new buyer was added |
| Invoice Created (`invoice.created`) | An invoice was made, by you or by a checkout |
| Invoice Expired (`invoice.expired`) | Nothing arrived in time |
| Invoice Held for Review (`invoice.review`) | A payment waits for your review before it counts |
| Payment Seen (`payment.received`) | A transfer was seen on chain, not yet confirmed |
| Checkout Completed (`checkout.completed`) | A checkout session was paid |
| Subscription Past Due (`subscription.past_due`) | A renewal was not paid on time |
| Subscription Canceled (`subscription.canceled`) | A subscription was canceled; it may run to the end of its period |
| Access Granted (`entitlement.granted`) | A buyer was given access to a product |
| Access Revoked (`entitlement.revoked`) | A buyer's access to a product ended |
| License Key Issued (`license.created`) | A license key was issued for a purchase |
| Credits Low (`credits.low`) | A customer's credits fell to the low-balance threshold |

Deliver on **Invoice Paid**. Put your own order or user ID in the invoice's metadata when you create it; it comes back on every event about that invoice. Each event has a unique `id`, and Perly Pay retries a delivery that is not answered for up to 72 hours, so a step that must happen once can skip an `id` it has already seen.

> **Note:** Perly Pay sends events only to an address it can reach: with a `sk_live_…` key that is a public `https` address, so n8n has to be reachable from the internet.

Every delivery's `Perly-Signature` is checked against the raw body before the workflow starts; a bad one is answered 401 and starts nothing. Every other delivery is claimed once from Perly Pay with your key, and Perly Pay's copy is what runs: the same delivery posted again starts nothing. The webhook's signing secret stays in the workflow's static data, never in a node's output.

## 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 package 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 test key, create an invoice (with the node, or in the dashboard) and pay it with test coins, or from the API with the invoice's id — an HTTP Request node will do:

```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 '{}'
```

The trigger then receives `invoice.fulfilled` as it would for a real payment.

## Go live

- Turn on live mode in Perly Pay and connect your wallet's public keys.
- Add a credential with a `sk_live_…` key and switch the workflow's Perly Pay nodes to it. Activating the workflow adds the live webhook.
- Pay one small real invoice and watch it arrive.
