Webhooks

Learn how to receive webhooks from Pluto for asynchronous events.

Webhooks are HTTPS push notifications that are triggered on the completion of asynchronous events. They are useful for executing logic after an event, such as shipping an order after a payment succeeds (payment_intent.succeeded). To receive Pluto webhooks, you will need to set up an endpoint on your server to receive them.

Create a webhook

In the below example, we create a simple Express server to receive webhooks from Pluto upon payment success and failure.

const express = require('express');
const app = express();

router.post('/webhook', express.raw({ type: 'application/json' }), (req, res, next) => {
  try {
    if (event.type === 'payment_intent.succeeded') {
      const paymentIntent = event.data.id;
      console.log('Payment succeeded', paymentIntent.id);
    }

    if (event.type === 'payment_intent.failed') {
      const paymentIntent = event.data.id;
      console.log('Payment failed', paymentIntent.id);
    }

    res.sendStatus(status.ACCEPTED);
  } catch (err) {
    next(err);
  }
});

app.listen(7000, () => console.log(`Listening on port 7000`));

Listen for events

When testing in development, we recommend using a third-party service such as Svix to forward events to your machine. Svix to forward Pluto webhooks to http://localhost:7000/webhook.

const { Pluto } = require('@plutohq/pluto-node');
const pluto = new Pluto(process.env.PLUTO_SECRET_KEY);

// Development
const webhook = await pluto.webhooks.create({
  endpoint_url: 'https://play.svix.com/in/c_4bnIA93aUc48DjsOCX6fEcxz',
  event_types: ['*'],
});

// Production
const webhook = await pluto.webhooks.create({
  endpoint_url: 'https://api.my-web3-site.com/webhook',
  event_types: ['payment_intent.succeeded', 'payment_intent.failed'],
});

Verify webhook signature

Since Pluto uses Svix to deliver webhooks, you can verify the request with our Node library by passing the Svix-Signature header and your endpoint secret. You can retrieve your endpoint secret by visiting the Pluto dashboard.

const express = require('express');
const app = express();

const secret = 'whsec_...';

router.post('/webhook', express.raw({ type: 'application/json' }), (req, res, next) => {
  try {
    const signature = request.headers['Svix-Signature'];

    await pluto.webhooks.verify({ secret, signature })
      .catch((e) => res.sendStatus(status.BAD_REQUEST));

    if (event.type === 'payment_intent.succeeded') {
      const paymentIntent = event.data.id;
      console.log('Payment succeeded', paymentIntent.id);
    }

    if (event.type === 'payment_intent.failed') {
      const paymentIntent = event.data.id;
      console.log('Payment failed', paymentIntent.id);
    }

    res.sendStatus(status.ACCEPTED);
  } catch (err) {
    next(err);
  }
});

app.listen(7000, () => console.log(`Listening on port 7000`));

Event types

Event types are named according to the schema {resource}.{event}, for example customer.created. By default, every resource creates a webhook for the created, updated, and deleted events. However, Invoices, PaymentIntents, and Subscriptions have additional events to provide additional tracking throughout the payment flow.

  • account.created
  • account.updated
  • account.deleted
  • api_key.created
  • api_key.updated
  • api_key.deleted
  • customer.created
  • customer.updated
  • customer.deleted
  • fee.created
  • fee.updated
  • fee.deleted
  • invoice.created
  • invoice.updated
  • invoice.deleted
  • invoice.open
  • invoice.past_due
  • invoice.paid
  • invoice.uncollectible
  • invoice.void
  • notification.created
  • notification.updated
  • notification.deleted
  • payout_wallet.created
  • payout_wallet.updated
  • payout_wallet.deleted
  • price.created
  • price.updated
  • price.deleted
  • product.created
  • product.updated
  • product.deleted
  • subscription.created
  • subscription.updated
  • subscription.deleted
  • subscription.incomplete
  • subscription.trialing
  • subscription.active
  • subscription.past_due
  • subscription.canceled
  • payment_intent.created
  • payment_intent.updated
  • payment_intent.deleted
  • payment_intent.requires_confirmation
  • payment_intent.succeeded
  • payment_intent.failed
  • payment_intent.canceled
  • transfer.created
  • transfer.updated
  • transfer.deleted
  • wallet.created
  • wallet.updated
  • wallet.deleted
  • webhook.created
  • webhook.updated
  • webhook.deleted

Webhook event schema

All webhook events objects follow the same schema:

  • account: account that the updated resource belongs to
  • data: updated document
  • type: type of event
  • test: boolean for determining if event was in test mode
  • created: timestamp of event creation
  • id: event ID

Below is an example webhook payload for a successful payment intent. To test your integration, you can send test events from the dashboard.

{
  "account": "acct_OxsviweKXYkKSm9BSpxXS",
  "type": "payment_intent.succeeded",
  "data": {
    "account": "acct_OxsviweKXYkKSm9BSpxXS",
    "platform_account": "acct_oCV7GPmaqJzHCWu8FoxGP",
    "hash": "0x98c6e5ec26a67567bf56f53e4eac338ab1f5fe0ee4207596be1699edf4aec808",
    "invoice": "in_1du9TffWXu8MwFviisKW0",
    "wallet": "wal_C7I3BMppEiRMITRwmQUJQ",
    "application_fee_percent": 1.5,
    "status": "succeeded",
    "last_payment_error": null,
    "test": false,
    "line_items": [],
    "created": "2022-07-04T17:30:49.120Z",
    "price": "price_eCw3GgFw8iY9BmVuVLNRm",
    "subscription": "sub_xqG7R1k68o7gXXQl7WGrb",
    "customer": "cus_5NGewrkxHQPAijhZ8CAFW",
    "currency": "eth",
    "chain": "eth",
    "amount": 0.05,
    "exchange_rate": 112522,
    "usd_amount": 5626,
    "fees": [
      {
        "account": "acct_OxsviweKXYkKSm9BSpxXS",
        "platform_account": "acct_oCV7GPmaqJzHCWu8FoxGP",
        "internal": true,
        "payment_intent": "pi_LmRMhqa9ufEAZoDmq9pu3",
        "percent": 1.5,
        "description": "Pluto platform fee",
        "test": false,
        "created": "2022-07-04T17:30:50.330Z",
        "wallet": "pwal_FsJufgsM21sy8svWaYAps",
        "object": "fee",
        "id": "fee_Dclp1r1wSChwpcCZ1FxxR"
      },
    ],
    "transfers": [],
    "object": "payment_intent",
    "id": "pi_LmRMhqa9ufEAZoDmq9pu3"
  },
  "test": false,
  "created": "2022-07-04T17:31:46.009Z",
  "object": "event",
  "id": "evt_1ws30H7TMlWGKCodVZPvf"
}