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:
- Open Webhooks.
- Create an endpoint.
- Enter your publicly reachable callback URL, e.g.
https://merchant.example/api/tariapay/webhook. - Select the events you want to receive.
- Save the signing secret and store it in your server's environment variables.
We recommend subscribing to at least:
payment_intent.confirmedpayment_intent.failedpayment_intent.refundedrefund_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
| Header | Description |
|---|---|
x-tariapay-signature | HMAC-SHA256 signature |
x-tariapay-timestamp | Unix timestamp in seconds |
x-tariapay-event | Event name |
x-tariapay-delivery-id | Unique delivery ID |
Signature verification with the SDK
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
{
"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.createdsubscription.renewedsubscription.past_duesubscription.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
paymentIdororderIdfor idempotent updates. - Return
2xxquickly; 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
- API fields: API reference
- Pre-launch checks: Go-live checklist