> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dmly.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Connect a payment gateway

> Connect Stripe, PayPal, Paystack, Razorpay, MyFatoorah or Mercado Pago, share a pay link on WhatsApp, and understand what a missed webhook means.

A payment gateway is what lets a client pay you online. Once one is connected, DMLY can mint a
pay link for an invoice, a booking or a subscription, and you send that link to the client on
WhatsApp. Until a gateway is connected you will see **No payment gateway is connected yet. Connect
one to generate a payment link.** and you can only record payments you took yourself.

Two things to know before you start, because both cost real money if you get them wrong:

<Warning>
  **DMLY does not register the webhook for you.** For every gateway, you must add the webhook
  endpoint yourself in the provider's own dashboard. Without it, a client can pay successfully at
  the provider and the invoice in DMLY will still say unpaid — see [What a failed webhook
  means](#what-a-failed-webhook-means).
</Warning>

<Note>
  DMLY has no default-gateway setting. When nothing picks a gateway explicitly, invoice pay links
  follow a fixed provider precedence — Stripe, then PayPal, Paystack, Razorpay, MyFatoorah, Mercado
  Pago — while bookings and subscription card-capture links use the first connected gateway
  alphabetically: Mercado Pago, MyFatoorah, PayPal, Paystack, Razorpay, Stripe. Connection order is
  irrelevant to both. If you run more than one gateway, expect invoices and bookings to land in
  different accounts.
</Note>

## Gateways you can connect

Six gateways are built. Anything else is not supported.

| Gateway      | Credentials you paste                                                           |
| ------------ | ------------------------------------------------------------------------------- |
| Stripe       | `secret_key`, `webhook_secret`                                                  |
| PayPal       | `client_id`, `client_secret`, `environment` (`sandbox` or `live`), `webhook_id` |
| Paystack     | `secret_key`                                                                    |
| Razorpay     | `key_id`, `key_secret`, `webhook_secret`                                        |
| MyFatoorah   | `api_token`, `base_url`, `webhook_secret`                                       |
| Mercado Pago | `access_token`, `webhook_secret`                                                |

There is no "sign in with Stripe" button. Connecting is always copying API credentials out of the
provider's dashboard and pasting them into DMLY. The credentials you paste are checked when you
connect, so a typo fails at that point rather than at the first real payment.

## Connect a gateway

<Steps>
  <Step title="Get your credentials from the provider">
    Sign in to the provider's own dashboard and copy the fields listed above for your gateway.
    Use live credentials for real money and the provider's test credentials while you are trying
    things out.
  </Step>

  <Step title="Paste them into DMLY">
    Go to **Configurations → Integrations** and open your gateway's tile — it holds the credential
    fields. Paste each credential into its field and save. DMLY validates them on save.

    Finance → Settings → Gateways is only a signpost. It always shows **Connect a payment gateway to
    accept online payments and share pay links.** and an **Open integrations** link, whether or not a
    gateway is connected, so use that link to get to the tile.
  </Step>

  <Step title="Add the webhook endpoint in the provider's dashboard">
    This step is not optional and DMLY cannot do it for you. The gateway's tile displays the webhook
    URL to paste into the provider — connecting a gateway prompts you to set that URL in the
    provider's own dashboard. Add it there and paste the resulting signing secret back into DMLY
    (`webhook_secret`, or `webhook_id` for PayPal). Paystack is the exception — it has no webhook
    secret field.
  </Step>

  <Step title="Take one real payment end to end">
    Send yourself a small invoice, pay it, and check the invoice flips to **Paid** on its own. If it
    doesn't, the webhook is not reaching DMLY — fix that now, not after a client pays.
  </Step>
</Steps>

## Take a payment

Most payments are collected against an invoice, and a Subscription always bills through one. But a
booking is charged directly against the booking, with no invoice behind it, and **Request payment**
— in the inbox, as a flow node, or as a quick automation action — mints a link for any amount with
no invoice either, as does **Send payment link** in amount mode. Those payments still land in the
same ledger. An Order gets an invoice only when you use its **Generate invoice** action.

The steps below are the invoice route.

<Steps>
  <Step title="Send the invoice">
    Sending an invoice moves it from **Draft** to **Sent** and messages the client. It goes out on
    WhatsApp by default, using your approved `dmly_invoice_sent` template. See
    [Invoices](/finance/invoices).
  </Step>

  <Step title="Share the pay link">
    On the invoice, **Payment link & QR** gives you the link. **Share link** offers **Share via**
    → **WhatsApp**, **SMS** or **Email**, plus **Copy link** and a QR code you can show on a phone
    or print. The QR is just the pay link in visual form.
  </Step>

  <Step title="Let the client pay">
    The link opens the gateway's own hosted checkout page. When the payment succeeds, the provider
    calls DMLY's webhook, DMLY records the payment against the invoice and recalculates **Balance
    due**. You don't mark it paid yourself.
  </Step>
</Steps>

The pay link is always for the current **Balance due**. If a client part-pays and you share the
link again, DMLY re-mints it for what's still owed rather than charging the original amount twice.

<Accordion title="The pay link won't generate">
  A pay link needs an invoice that can actually be paid. You'll get an error instead of a link when
  the invoice is **Void**, when the invoice has no contact attached, or when the balance is already
  zero.
</Accordion>

### Payments you took yourself

Cash, a bank transfer, a card machine — record these on the invoice and they land in the same
ledger as gateway payments, marked as **Succeeded** immediately. The methods are **Cash**, **Bank
transfer**, **Credit / debit card**, **Cheque**, **EFTPOS** and **Other**. You can also record a
payment from a client's profile, see [Client profile](/contacts/client-profile).

## Payment statuses

| Status        | Means                                                                                                                      |
| ------------- | -------------------------------------------------------------------------------------------------------------------------- |
| **Pending**   | A pay link exists; the money hasn't arrived.                                                                               |
| **Succeeded** | Paid. The invoice's paid and due amounts are recalculated from succeeded payments only.                                    |
| **Failed**    | The attempt was declined or abandoned.                                                                                     |
| **Refunded**  | The payment is marked refunded in DMLY's ledger and reversed on the client's statement. DMLY does not send the money back. |

Statuses only move forward: Pending → Failed → Succeeded → Refunded. A late or out-of-order message
from the provider can't drag a paid invoice back to pending.

<Warning>
  **Refunding in DMLY is bookkeeping only.** The Refund button — and its confirmation, **This will
  refund the payment. Are you sure you want to continue?** — marks the payment refunded and reverses
  it on the client's statement. It never contacts the gateway, so the client's card is not touched.
  You must issue the actual refund in the provider's own dashboard. The reverse direction does work
  on its own: a refund you issue at the provider arrives as a webhook and flips the payment to
  **Refunded** in DMLY.
</Warning>

## Sandbox behaviour

When the install runs in sandbox, no gateway is ever contacted. Pay links are fake, predictable
URLs shaped like `https://pay.demo/<provider>/<reference>`, and a charge always succeeds. This lets
you walk the whole flow — invoice, link, payment, receipt, statement — without a provider account
or real money.

<Note>
  Sandbox only skips webhook signature checks on a local or test install. On a live install, an
  incoming webhook must carry a valid signature no matter what, so nobody can post a fake "paid"
  message to your workspace.
</Note>

## What a failed webhook means

The webhook is how the gateway tells DMLY that money moved. Everything downstream — the invoice
going **Paid**, a held appointment slot being confirmed, a receipt — hangs off it. So a webhook
problem doesn't lose the client's money, it loses DMLY's knowledge of it.

**The webhook never arrives** (endpoint not added in the provider's dashboard, or the wrong URL).
The client is charged, the provider shows the payment, DMLY shows the invoice still unpaid. Nothing
retries, because nothing arrived. Fix the endpoint in the provider's dashboard; new payments will
then reconcile on their own.

**The webhook is rejected** (wrong `webhook_secret` / `webhook_id`, or copied from a different
environment). Same visible symptom: paid at the provider, unpaid in DMLY. Re-copy the signing
secret from the provider into DMLY.

**The webhook arrives but errors.** DMLY retries it. A payment message that fails mid-processing is
not treated as already-handled on the retry, so it isn't silently dropped.

**The payment doesn't match anything DMLY expected** — for example a charge started outside DMLY.
It is still recorded as a payment rather than discarded, so the money is visible even if it isn't
attached to an invoice.

<Warning>
  If a payment is missing because of a webhook problem, fix the webhook rather than recording the
  same money manually. An invoice adds up every succeeded payment on it, so if you record it by
  hand and the gateway message later arrives, the client will show as having paid twice.
</Warning>

<Accordion title="An appointment was paid for but isn't confirmed">
  For a booking that requires payment first, the slot is held and only confirmed when the payment
  succeeds — which DMLY learns from the webhook. If the webhook doesn't arrive, the hold lapses and
  the slot frees up even though the client paid. If a payment for a lapsed hold does arrive, it's
  flagged rather than thrown away. See [Appointment payments](/appointments/payments).
</Accordion>

## Saved cards and automatic renewals

Subscriptions can charge a saved card automatically on Stripe, PayPal, Paystack, Razorpay and
MyFatoorah. The card is captured through a separate setup link, and no payment is recorded against
the subscription at that point. If a renewal charge fails, DMLY retries every 2 days and pauses the
subscription after 3 failures, notifying the client each time. See
[Subscriptions](/finance/subscriptions).

Stripe, PayPal and Razorpay capture the card without charging it. Paystack and MyFatoorah tokenise
the card with a small real charge on the hosted page, so on those two the client does see a charge.

<Warning>
  **Mercado Pago cannot auto-charge.** It supports pay links only, so a subscription on Mercado Pago
  has to be collected with a pay link each cycle rather than renewing on its own.
</Warning>

## Related

<Columns cols={2}>
  <Card title="Invoices" icon="file-invoice" href="/finance/invoices">
    Create, send, part-pay and void an invoice.
  </Card>

  <Card title="Finance settings" icon="gear" href="/finance/settings">
    Default currency, due days and numbering.
  </Card>

  <Card title="Subscriptions" icon="rotate" href="/finance/subscriptions">
    Recurring billing and automatic renewals.
  </Card>

  <Card title="Appointment payments" icon="calendar-check" href="/appointments/payments">
    Charge before or after a booking.
  </Card>
</Columns>
