Skip to content

Receiving payment results

Webhooks are the primary path for syncing payment results into your system. After a buyer completes payment, Taria Pay sends events to the webhook endpoint you configure.

Why webhooks are essential

If your site has order statuses, shipping, invoicing, membership access, or subscription entitlements, you should configure webhooks.

Don't rely solely on:

  • Whether the buyer returned to your successUrl
  • Whether the frontend page showed success
  • Support staff manually checking transactions

Your actual business records should be driven by webhooks or the query API.

Creating a webhook endpoint

In the Dashboard, go to Developers, then:

  1. Open Webhooks.
  2. Create an endpoint.
  3. Enter your publicly reachable callback URL, e.g. https://merchant.example/api/tariapay/webhook.
  4. Select the events you want to receive.
  5. Save the signing secret and store it in your server's environment variables.

We recommend subscribing to at least:

  • payment_intent.confirmed
  • payment_intent.failed
  • payment_intent.refunded
  • refund_request.created (if you want buyer refund requests synced into your own support system)

Whether the buyer pays with a wallet on the checkout page or with a direct transfer, the event types and payload structure are identical — your webhook handler doesn't need to distinguish between payment methods.

Request headers

HeaderDescription
x-tariapay-signatureHMAC-SHA256 signature
x-tariapay-timestampUnix timestamp in seconds
x-tariapay-eventEvent name
x-tariapay-delivery-idUnique delivery ID

Signature verification with the SDK

ts
import { TariaPay } from "@tariapay/sdk";

const tariapay = new TariaPay({
  secretKey: process.env.TARIAPAY_SECRET_KEY!,
});

export async function POST(req: Request) {
  const rawBody = await req.text();

  const event = tariapay.webhooks.constructEvent(
    rawBody,
    req.headers,
    process.env.TARIAPAY_WEBHOOK_SECRET!,
  );

  const paymentIntent = event.data.paymentIntent;

  switch (event.type) {
    case "payment_intent.confirmed":
      // Idempotently update the order by paymentId or orderId, then fulfill
      break;
    case "payment_intent.failed":
      // Mark the order failed and let the buyer pay again
      break;
    case "payment_intent.refunded":
      // Sync refund, inventory, and after-sales status
      break;
    case "refund_request.created":
      // Record data.refundRequest for merchant review; a request does not mean funds have been refunded
      break;
    default:
      break;
  }

  return Response.json({ received: true });
}

Example payload

json
{
  "id": "evt_123",
  "type": "payment_intent.confirmed",
  "createdAt": "2026-03-29T12:00:00.000Z",
  "data": {
    "paymentIntent": {
      "paymentId": "b1985d0b-2026-4f8c-bf75-59ea53fe5466",
      "orderId": "order_1001",
      "status": "confirmed",
      "currency": "USDC",
      "amount": "49.99",
      "checkoutUrl": "https://checkout.tariapay.com/pi_b1985d0b-2026-4f8c-bf75-59ea53fe5466",
      "txHash": "0x..."
    }
  }
}

Subscription events

If you use Subscriptions (API), you can also subscribe to the following events to keep subscription status in sync:

  • subscription.created
  • subscription.renewed
  • subscription.past_due
  • subscription.canceled

Their data contains subscription and invoice; use subscription.merchantCustomerId to link back to the customer in your own system.

Handling recommendations

  • Verify the signature before processing the payload.
  • Use paymentId or orderId for idempotent updates.
  • Return 2xx quickly; push slow work to your background queue.
  • Handle duplicate deliveries so you never ship twice or grant entitlements twice.
  • Check Delivery Logs for failure reasons and retry history.

Next steps