Skip to content

serverTracker.trackRefund()

Server-side refund. The common entry point for refunds — most refunds originate from an admin panel or a payment-provider webhook, not from the browser.

GA4-only by design. The ads destination always returns { skipped: true, reason: 'refund_ads_unsupported' }. Ads refund adjustments use a different API endpoint (uploadConversionAdjustments) that the SDK does not implement in v1.

serverTracker.trackRefund(input: ServerRefundInput): Promise<ServerHelperResult>;
type ServerRefundInput = {
transactionId: string;
value?: number;
currency?: string;
items?: TrackbridgeItem[];
affiliation?: string;
coupon?: string;
shipping?: number;
tax?: number;
clientId: string;
userId?: string;
userData?: UserData;
consent?: ServerConsent;
};
FieldRequiredNotes
transactionIdyesMust match the original trackPurchase transactionId.
clientIdyesGA4 client ID — typically the value persisted on the order row at checkout.
valuenoRefund amount. Omit for a full refund (GA4 infers from transactionId).
currencynoISO 4217. Required by GA4 when value is set.
itemsnoItems being refunded — for partial refunds.
affiliation, coupon, shipping, taxnoGA4 params.
userId, userData, consentnoStandard.

Promise<ServerHelperResult>. The ads field is always:

{ skipped: true, reason: 'refund_ads_unsupported' }

The ga4 field carries { ok } | { ok: false, error }.

export async function POST(req: Request) {
const event = await verifyStripeSignature(req);
if (event.type !== 'charge.refunded') return new Response();
const order = await db.orders.findByChargeId(event.data.object.id);
const result = await serverTracker.trackRefund({
transactionId: order.id,
value: event.data.object.amount_refunded / 100,
currency: order.currency,
clientId: order.gaClientId,
});
if (!result.ga4.ok && !('skipped' in result.ga4)) reportError(result.ga4.error);
return new Response();
}