# Invoice payment links

Turn any emitted e-invoice into money in the bank: one link, a hosted pay page, card or
"Pay with BaxPay" — and the lifecycle plus e-reporting update themselves.

A payment link is an unguessable URL (`https://pay.baxpay.com/pl_…`) pointing to a hosted
page that shows the payer only what they need — seller, invoice number, due date, amount — and
lets them pay. No BaxPay account is required for card payment.

## Create a link

- `POST /einvoices/{id}/payment-link` — Idempotency-Key required

Works on any **sent** OUTBOUND invoice that isn't paid yet; calling it again returns the same link.
In sandbox, first emit an invoice to collect:

```bash
INVOICE=$(curl -s -X POST https://sandbox-api.baxpay.com/v1/einvoices \
  -H "Authorization: Bearer $BAXPAY_API_KEY" -H "Idempotency-Key: $(uuidgen)" -H "Content-Type: application/json" \
  -d '{
    "invoiceNumber": "FA-2026-0050", "issueDate": "2026-09-01", "dueDate": "2026-09-30",
    "buyerName": "ACME SAS", "buyerSiren": "552100554", "buyerVatId": "FR96552100554",
    "buyerAddress": { "line1": "12 rue de la Paix", "city": "Paris", "postalCode": "75002", "countryCode": "FR" },
    "lines": [{ "lineNumber": 1, "description": "Conseil", "quantity": 3, "unitCode": "DAY", "unitPrice": 650, "vatRate": 20 }]
  }' | jq -r .data.id)

curl -X POST "https://sandbox-api.baxpay.com/v1/einvoices/$INVOICE/send" \
  -H "Authorization: Bearer $BAXPAY_API_KEY" -H "Idempotency-Key: $(uuidgen)"
```

Then create its link:

```bash
curl -X POST https://sandbox-api.baxpay.com/v1/einvoices/0d1f9b1e-…/payment-link \
  -H "Authorization: Bearer $BAXPAY_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"
```

```json
{
  "success": true,
  "data": {
    "token": "pl_4f1f0c4c4e1a…",
    "url": "https://pay.baxpay.com/pl_4f1f0c4c4e1a…"
  }
}
```

Share the URL however you like: it is also the natural thing to print on the PDF or paste into a
payment-terms email.

## What the payer sees

The hosted page offers up to two methods:

- **Card** — a guest card form, processed by the configured acquirer. If no acquirer is
  contracted on your live environment the method is hidden and the API answers `503` — BaxPay
  never fakes a live charge.
- **Pay with BaxPay** — the payer signs in to their own BaxPay account and pays with their
  wallet, card or account. Strong Customer Authentication (SCA) applies exactly as in the
  standard [payments flow](https://developers.baxpay.com/payments); BaxPay then verifies the captured transaction matches the
  invoice (right seller, right amount) before marking anything paid.

## What happens on success

1. The invoice is stamped paid (`paidAt`, method, transaction reference) — atomically, so a
   double payment race is impossible.
2. The lifecycle advances `PAYMENT_TRANSMITTED` → `COLLECTED` (*encaissée* — a mandatory status
   of the reform). BaxPay may assert it because it processed the encaissement itself.
3. The `COLLECTED` event flows into your `PAYMENT_DATA` [e-reporting](https://developers.baxpay.com/einvoicing#e-reporting).
4. Your webhooks receive `einvoice.paid`:

```json
{
  "id": "5d0c4e2a-…",
  "type": "einvoice.paid",
  "livemode": true,
  "data": {
    "invoiceId": "0d1f9b1e-…",
    "invoiceNumber": "FA-2026-0042",
    "amount": 2340,
    "currency": "EUR",
    "method": "CARD_NOT_PRESENT",
    "transactionRef": "a7c9…"
  },
  "createdAt": "2026-09-12T09:14:03.000Z"
}
```

`method` names the card or wallet method the payer used.

## Public endpoints behind the page

You normally never call these — the hosted page does — but they are public and documented for
completeness:

- `GET /pay/einvoice/{token}`

```bash
curl https://sandbox-api.baxpay.com/v1/pay/einvoice/pl_4f1f0c4c4e1a…
```

```json
{
  "success": true,
  "data": {
    "sellerName": "…",
    "invoiceNumber": "FA-2026-0001",
    "amountDue": 2340,
    "currency": "EUR",
    "status": "PAYABLE",
    "livemode": false,
    "methods": {}
  }
}
```

The payer-safe summary: seller name, invoice number, dates, amounts, lines, the payment methods on
offer, and whether it can be paid (`status`: `PAYABLE`, `PAID` or `NOT_PAYABLE`). Never artifacts or
buyer data.

- `POST /pay/einvoice/{token}/card`

Guest card charge, tightly rate-limited. When the merchant's card payments go through BaxPay's
hosted checkout it answers `400`, and the page takes the payer through that checkout instead.

- `POST /pay/einvoice/{token}/confirm-baxpay`

Called by BaxPay's own hosted page on behalf of the signed-in payer — never by your
integration. It links the payer's captured transaction to the invoice after verifying merchant,
amount and ownership.

## Sandbox

On a test invoice the card form takes the [test cards](https://developers.baxpay.com/sandbox#test-cards): `4242 4242 4242 4242`
succeeds and **`4000 0000 0000 0002` always declines**, so you can test both paths. Nothing reaches
a card network, a bank or the e-invoicing network:

```bash
curl -X POST https://sandbox-api.baxpay.com/v1/pay/einvoice/pl_4f1f0c4c4e1a…/card \
  -H "Content-Type: application/json" \
  -d '{ "number": "4000 0000 0000 0002", "expMonth": 12, "expYear": 2030, "cvc": "123", "holder": "ACME SAS" }'
```

```bash
curl -X POST https://sandbox-api.baxpay.com/v1/pay/einvoice/pl_4f1f0c4c4e1a…/card \
  -H "Content-Type: application/json" \
  -d '{ "number": "4242 4242 4242 4242", "expMonth": 12, "expYear": 2030, "cvc": "123", "holder": "ACME SAS" }'
```

```json
{ "success": true, "data": { "status": "PAID", "maskedInstrument": "•••• 4242" } }
```

Test payments fire test-mode webhooks only, and the invoice moves to `COLLECTED`.

> **Cross-selling tip**
> The pay page is also where invoice recipients discover BaxPay: "Pay with BaxPay" works for any
> buyer who has an account, and the card path requires nothing at all.
