· 5 min · TextMeFlow Team

WhatsApp Payment Confirmations from PayPal Webhooks

PayPal is still the default checkout button for a lot of webshops and freelance invoicing, but its confirmation email is easy to miss in a crowded inbox. PayPal's own Webhooks API already tells you the moment a payment completes, fails or gets refunded — the missing piece is turning that event into a message the customer actually sees. Here's how to wire PayPal straight to the TextMeFlow API, without a third-party automation tool in between.

Why go straight from PayPal to the API

PayPal has supported webhooks for years, but most "WhatsApp for PayPal" setups route through a generic automation platform that adds its own delay, its own dashboard, and a middleman holding your payment data. PayPal's webhook payload already carries the payer's name, amount, currency and transaction ID — enough to build a confirmation message with one small receiver script. That script posts to TextMeFlow's messages API, so the only ongoing cost is your existing plan's message quota.

Step 1: Subscribe to the right event types

In the PayPal Developer Dashboard, under your app's Webhooks settings, add an endpoint URL and select only the events worth a customer-facing message:

  • PAYMENT.CAPTURE.COMPLETED — payment confirmation
  • PAYMENT.CAPTURE.DENIED — failed payment alert
  • PAYMENT.CAPTURE.REFUNDED — refund confirmation
  • CUSTOMER.DISPUTE.CREATED — internal alert, not customer-facing — route this to your own team's number instead

Subscribing to every available event type just means filtering out noise later. Pick the handful that map to an actual message you want to send.

Step 2: Verify the webhook before trusting it

PayPal signs each webhook with a certificate and a transmission signature (PAYPAL-TRANSMISSION-SIG, PAYPAL-CERT-URL, PAYPAL-TRANSMISSION-ID), verified against PayPal's /v1/notifications/verify-webhook-signature endpoint. Never skip this step — an unverified webhook URL is a public endpoint anyone can POST to. The same discipline applies on the way back from TextMeFlow: always check the HMAC signature on TextMeFlow's own webhooks before acting on an incoming event.

app.post('/webhooks/paypal', express.json(), async (req, res) => {
  const verified = await verifyPaypalSignature(req.headers, req.body);
  if (!verified) return res.status(400).send('signature verification failed');

  const event = req.body;
  if (event.event_type === 'PAYMENT.CAPTURE.COMPLETED') {
    const capture = event.resource;
    const phone = capture.custom_id; // see step 3 on where this comes from
    if (phone) await sendPaymentConfirmation(phone, capture);
  }

  res.status(200).send('ok');
});

Step 3: Get a phone number into the payload

PayPal's capture object doesn't include a phone number by default. The reliable way to get one into the webhook is to set it as custom_id (or inside purchase_units[].custom_id) when you create the order, before redirecting the buyer to PayPal — pulled from your checkout form or your own customer record. Without that step, the webhook fires but your receiver has nothing to send a message to, so this has to be solved at checkout time, not in the webhook handler.

Step 4: Send the confirmation

curl -X POST https://api.textmeflow.eu/v1/messages \
  -H "Authorization: Bearer $TEXTMEFLOW_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+32470123456",
    "text": "Hi Sarah, we received your PayPal payment of €49.00 for order #ORD-3312. Thanks for your business!"
  }'

For a denied capture, keep the wording practical instead of alarming — "your PayPal payment could not be completed, please try again or use a different card" lands better than anything that reads as a warning. If you send a PDF receipt alongside the text, the same endpoint accepts attachments up to 100 MB in one call.

Step 5: Handle retries and opt-outs correctly

Two things matter specifically for a PayPal integration:

  1. Deduplicate on webhook ID. PayPal retries undelivered webhooks on its own schedule. If your receiver also has retry logic, you can end up sending the same confirmation twice — track the webhook's id field and skip anything already processed.
  2. Respect STOP. A payment doesn't override a previous opt-out. TextMeFlow blocks sends to a number that has opted out, no matter which event triggered the send — see how STOP handling fits into the broader anti-spam pipeline.

A payment confirmation is a one-to-one transactional message triggered by the customer's own action, so it sits outside the broadcast-style checks that apply to bulk sends — but STOP and deduplication still apply to every message regardless of trigger.

Beyond the confirmation email

PayPal already emails a receipt the moment a capture completes, so this isn't a replacement — it's the message your customer actually reads in the first few minutes after paying. Delivery and read receipts flow back through TextMeFlow's own webhook, so you can tell whether a confirmation was seen rather than just sent. The same receiver pattern extends to subscription billing events if you use PayPal for recurring payments, or to a refund-issued message when a return is processed on your end.

Start free on TextMeFlow — 50 messages a month, no time limit, enough to build and test a full PayPal confirmation flow before committing to a paid plan.

Zelf WhatsApp-berichten versturen via API?

Gratis voor altijd tot 50 berichten/maand. QR scannen en binnen 5 minuten verstuur je je eerste bericht.

Gratis voor altijd