{"version":3,"sources":["../../src/core/errors.ts","../../src/webhooks/verify.ts","../../src/webhooks/express.ts","../../src/webhooks/next.ts","../../src/types/enums.ts"],"names":["createHmac","timingSafeEqual"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { ApiTokenScope } from '../types/enums.js';\n\n/**\n * Reason an API token was rejected. Derived from the exact messages the backend\n * emits in `api-tokens.service.ts` / `guards/api-token.guard.ts`.\n */\nexport type AuthFailureReason =\n  | 'missing_bearer'\n  | 'invalid_format'\n  | 'invalid_token'\n  | 'token_inactive'\n  | 'token_expired'\n  | 'user_inactive'\n  | 'unknown';\n\n/** Why a 403 came back. See {@link GpmPayPermissionError}. */\nexport type PermissionFailureReason = 'scope' | 'endpoint' | 'ownership';\n\nexport type ConfigErrorCode =\n  | 'missing_api_token'\n  | 'invalid_api_token'\n  | 'invalid_api_token_format'\n  | 'invalid_base_url'\n  | 'invalid_argument';\n\nexport type WebhookSignatureFailureReason =\n  | 'missing_secret'\n  | 'malformed_header'\n  | 'timestamp_skew'\n  | 'mismatch';\n\nconst BRAND = Symbol.for('gpmpay.sdk.error');\n\n/**\n * Base class for everything this SDK throws.\n *\n * Generic over `code` so subclasses can narrow it (and give consumers\n * autocomplete on `error.code`) without redeclaring the field.\n */\nexport class GpmPayError<TCode extends string = string> extends Error {\n  readonly code: TCode;\n  /** @internal Cross-realm brand — `instanceof` breaks across CJS/ESM copies. */\n  readonly [BRAND] = true as const;\n\n  constructor(message: string, code: TCode) {\n    super(message);\n    this.name = new.target.name;\n    this.code = code;\n    Object.setPrototypeOf(this, new.target.prototype);\n  }\n\n  /**\n   * Prefer this over `instanceof` when a dual CJS/ESM install could put two\n   * copies of the class in one process.\n   */\n  static isGpmPayError(value: unknown): value is GpmPayError {\n    return (\n      typeof value === 'object' &&\n      value !== null &&\n      (value as Record<symbol, unknown>)[BRAND] === true\n    );\n  }\n}\n\n/** Bad SDK usage — thrown before any network call happens. */\nexport class GpmPayConfigError extends GpmPayError<ConfigErrorCode> {\n  constructor(message: string, code: ConfigErrorCode = 'invalid_argument') {\n    super(message, code);\n  }\n}\n\n/** DNS/TCP/TLS failure. `cause` holds the original error. */\nexport class GpmPayConnectionError extends GpmPayError {\n  /** Overrides the standard `Error.cause` so it is always populated here. */\n  override readonly cause: unknown;\n  /** Node's `err.cause.code`, e.g. `ECONNREFUSED`, `ENOTFOUND`. */\n  readonly syscallCode: string | undefined;\n\n  constructor(message: string, cause: unknown, syscallCode?: string) {\n    super(message, 'connection_error');\n    this.cause = cause;\n    this.syscallCode = syscallCode;\n  }\n}\n\nexport class GpmPayTimeoutError extends GpmPayError {\n  readonly timeoutMs: number;\n\n  constructor(message: string, timeoutMs: number) {\n    super(message, 'timeout');\n    this.timeoutMs = timeoutMs;\n  }\n}\n\nexport class GpmPayWebhookSignatureError extends GpmPayError {\n  readonly reason: WebhookSignatureFailureReason;\n\n  constructor(message: string, reason: WebhookSignatureFailureReason) {\n    super(message, 'webhook_signature');\n    this.reason = reason;\n  }\n}\n\nexport interface ApiErrorContext {\n  status: number;\n  requestId: string;\n  rawBody: unknown;\n  rawMessage: string | string[];\n}\n\n/** Any non-2xx HTTP response. */\nexport class GpmPayAPIError extends GpmPayError {\n  readonly status: number;\n  /** Correlation id sent as `X-GPMPay-Request-Id`. Quote it to support. */\n  readonly requestId: string;\n  readonly rawBody: unknown;\n  readonly rawMessage: string | string[];\n\n  constructor(message: string, ctx: ApiErrorContext, code = 'api_error') {\n    super(message, code);\n    this.status = ctx.status;\n    this.requestId = ctx.requestId;\n    this.rawBody = ctx.rawBody;\n    this.rawMessage = ctx.rawMessage;\n  }\n}\n\n/** 400 — request body failed validation. */\nexport class GpmPayBadRequestError extends GpmPayAPIError {\n  /** One entry per failed constraint, as produced by Nest's ValidationPipe. */\n  readonly validationMessages: string[];\n\n  constructor(\n    message: string,\n    ctx: ApiErrorContext & { validationMessages: string[] },\n  ) {\n    super(message, ctx, 'bad_request');\n    this.validationMessages = ctx.validationMessages;\n  }\n}\n\n/** 401 — the API token was rejected. */\nexport class GpmPayAuthenticationError extends GpmPayAPIError {\n  readonly reason: AuthFailureReason;\n\n  constructor(message: string, ctx: ApiErrorContext & { reason: AuthFailureReason }) {\n    super(message, ctx, 'authentication_error');\n    this.reason = ctx.reason;\n  }\n}\n\n/**\n * 403 — one of three things:\n *\n * - `scope` — the token exists but lacks the scope this route requires.\n * - `endpoint` — the route accepts no API token at all (dashboard-only).\n * - `ownership` — the resource exists but belongs to another account.\n */\nexport class GpmPayPermissionError extends GpmPayAPIError {\n  readonly missingScope: ApiTokenScope | undefined;\n  readonly reason: PermissionFailureReason;\n\n  constructor(\n    message: string,\n    ctx: ApiErrorContext & {\n      missingScope?: ApiTokenScope;\n      reason: PermissionFailureReason;\n    },\n  ) {\n    super(message, ctx, 'permission_error');\n    this.missingScope = ctx.missingScope;\n    this.reason = ctx.reason;\n  }\n}\n\nexport class GpmPayNotFoundError extends GpmPayAPIError {\n  /** Resource name parsed out of e.g. `\"Order not found\"`. */\n  readonly resource: string | undefined;\n\n  constructor(message: string, ctx: ApiErrorContext & { resource?: string }) {\n    super(message, ctx, 'not_found');\n    this.resource = ctx.resource;\n  }\n}\n\nexport class GpmPayRateLimitError extends GpmPayAPIError {\n  readonly retryAfterSeconds: number | undefined;\n\n  constructor(\n    message: string,\n    ctx: ApiErrorContext & { retryAfterSeconds?: number },\n  ) {\n    super(message, ctx, 'rate_limit');\n    this.retryAfterSeconds = ctx.retryAfterSeconds;\n  }\n}\n\nexport class GpmPayServerError extends GpmPayAPIError {\n  constructor(message: string, ctx: ApiErrorContext) {\n    super(message, ctx, 'server_error');\n  }\n}\n\n/** Ordered most-specific first — `Invalid token` must lose to `Invalid token format`. */\nconst AUTH_REASONS: [RegExp, AuthFailureReason][] = [\n  [/missing bearer token/i, 'missing_bearer'],\n  [/invalid token format/i, 'invalid_format'],\n  [/token is not active/i, 'token_inactive'],\n  [/token[_ ]expired/i, 'token_expired'],\n  [/user inactive/i, 'user_inactive'],\n  [/invalid token/i, 'invalid_token'],\n];\n\nfunction extractMessage(body: unknown): string | string[] {\n  if (typeof body === 'string' && body.trim() !== '') return body;\n  if (body !== null && typeof body === 'object') {\n    const b = body as { message?: unknown; error?: unknown };\n    if (Array.isArray(b.message) || typeof b.message === 'string') {\n      return b.message as string | string[];\n    }\n    if (typeof b.error === 'string') return b.error;\n  }\n  return '';\n}\n\n/**\n * Map an HTTP error response onto the typed error hierarchy.\n *\n * @remarks\n * The backend registers no global exception filter, so error bodies use Nest's\n * default shape (`{ statusCode, message, error }`) and are NOT wrapped in the\n * `{ statusCode, message, data }` success envelope.\n */\nexport function errorFromResponse(\n  status: number,\n  body: unknown,\n  requestId: string,\n  extra: { retryAfterSeconds?: number } = {},\n): GpmPayAPIError {\n  const raw = extractMessage(body);\n  const msg =\n    (Array.isArray(raw) ? raw.join('; ') : raw) || `HTTP ${String(status)}`;\n  const ctx: ApiErrorContext = {\n    status,\n    requestId,\n    rawBody: body,\n    rawMessage: raw === '' ? msg : raw,\n  };\n\n  switch (status) {\n    case 400:\n      return new GpmPayBadRequestError(msg, {\n        ...ctx,\n        validationMessages: Array.isArray(raw) ? raw : [msg],\n      });\n\n    case 401: {\n      const reason =\n        AUTH_REASONS.find(([re]) => re.test(msg))?.[1] ?? 'unknown';\n      return new GpmPayAuthenticationError(\n        `${msg} — check your API token is correct, ACTIVE and not expired.`,\n        { ...ctx, reason },\n      );\n    }\n\n    case 403: {\n      const scope = /missing scope:\\s*(\\S+)/i.exec(msg)?.[1];\n      if (scope) {\n        return new GpmPayPermissionError(\n          `API token is missing the \"${scope}\" scope. Regenerate the token with that scope enabled.`,\n          { ...ctx, missingScope: scope as ApiTokenScope, reason: 'scope' },\n        );\n      }\n      // `ApiTokenGuard` is fail-closed: a route that declares no `@ApiScopes()`\n      // rejects API tokens outright, whoever owns the resource. Reporting that\n      // as an ownership problem sends people hunting for the wrong bug.\n      if (/not available to api tokens/i.test(msg)) {\n        return new GpmPayPermissionError(\n          `${msg} — this endpoint is dashboard-only and no API token scope grants it.`,\n          { ...ctx, reason: 'endpoint' },\n        );\n      }\n      return new GpmPayPermissionError(\n        `${msg} — the resource exists but belongs to another account.`,\n        { ...ctx, reason: 'ownership' },\n      );\n    }\n\n    case 404: {\n      const resource = /^(.*?)\\s+not found/i.exec(msg)?.[1];\n      return new GpmPayNotFoundError(\n        msg,\n        resource === undefined ? ctx : { ...ctx, resource },\n      );\n    }\n\n    // No 409 case: every route this SDK can reach is either a read or a write\n    // with no uniqueness constraint, so a conflict has no source. A 409 from\n    // `client.request()` falls through to the generic GpmPayAPIError below.\n\n    case 429:\n      return new GpmPayRateLimitError(\n        msg,\n        extra.retryAfterSeconds === undefined\n          ? ctx\n          : { ...ctx, retryAfterSeconds: extra.retryAfterSeconds },\n      );\n\n    default:\n      return status >= 500\n        ? new GpmPayServerError(msg, ctx)\n        : new GpmPayAPIError(msg, ctx);\n  }\n}\n","import { createHmac, timingSafeEqual } from 'node:crypto';\n\nimport { GpmPayWebhookSignatureError } from '../core/errors.js';\nimport type { WebhookPayload } from '../types/webhook.js';\n\n/** Header carrying the signature. Overridable per webhook setting. */\nexport const SIGNATURE_HEADER = 'X-GPMPay-Signature';\n\n/** Header carrying the event name (WordPress driver). */\nexport const EVENT_HEADER = 'X-GPMPay-Event';\n\n/** Clock-skew window the backend also enforces. */\nexport const DEFAULT_TOLERANCE_SECONDS = 300;\n\nexport interface VerifyWebhookInput {\n  /**\n   * The **exact bytes** of the request body.\n   *\n   * Never `JSON.stringify(req.body)` — re-serializing a parsed body changes\n   * whitespace and key order, and the signature will never match.\n   */\n  rawBody: string | Buffer | Uint8Array;\n  /** Value of `X-GPMPay-Signature`: `t=<unix_seconds>,v1=<hex_sha256>`. */\n  signature: string;\n  /**\n   * `authorizationSecret` for an HTTP+HMAC webhook, or `wpSecret` for the\n   * WordPress driver.\n   */\n  secret: string;\n  /** Skew window in seconds. Default 300. Pass 0 to disable (tests only). */\n  toleranceSeconds?: number;\n  /** @internal Test seam. */\n  now?: () => number;\n}\n\nfunction toBuffer(input: string | Buffer | Uint8Array): Buffer {\n  if (Buffer.isBuffer(input)) return input;\n  if (typeof input === 'string') return Buffer.from(input, 'utf8');\n  return Buffer.from(input);\n}\n\n/**\n * Parse `t=…,v1=…`, splitting each pair on its **first** `=` only — hex and\n * base64 values may legitimately contain `=`.\n */\nfunction parseSignatureHeader(signature: string): Record<string, string> {\n  const parts: Record<string, string> = {};\n  for (const segment of signature.split(',')) {\n    const index = segment.indexOf('=');\n    if (index > 0) {\n      parts[segment.slice(0, index).trim()] = segment.slice(index + 1).trim();\n    }\n  }\n  return parts;\n}\n\n/**\n * Verify a webhook signature, throwing a typed error explaining exactly why it\n * failed.\n *\n * Matches `apps/backend/src/modules/webhook-delivery/hmac.util.ts`: the signed\n * string is `` `${t}.${rawBody}` ``, hashed with HMAC-SHA256 and compared in\n * constant time.\n *\n * @throws {GpmPayWebhookSignatureError}\n */\nexport function assertWebhookSignature(input: VerifyWebhookInput): {\n  timestamp: number;\n} {\n  const { signature, secret, now = Date.now } = input;\n  const tolerance = input.toleranceSeconds ?? DEFAULT_TOLERANCE_SECONDS;\n\n  if (typeof secret !== 'string' || secret === '') {\n    throw new GpmPayWebhookSignatureError(\n      'Webhook secret is empty. Pass the secret you configured on the webhook ' +\n        'setting (e.g. process.env.GPMPAY_WEBHOOK_SECRET).',\n      'missing_secret',\n    );\n  }\n\n  if (typeof signature !== 'string' || signature.trim() === '') {\n    throw new GpmPayWebhookSignatureError(\n      `Missing ${SIGNATURE_HEADER} header.`,\n      'malformed_header',\n    );\n  }\n\n  const parts = parseSignatureHeader(signature);\n  const timestamp = Number(parts.t);\n  const v1 = parts.v1;\n\n  if (!Number.isFinite(timestamp) || timestamp <= 0 || !v1) {\n    throw new GpmPayWebhookSignatureError(\n      `Malformed ${SIGNATURE_HEADER}: \"${signature}\". Expected \"t=<unix_seconds>,v1=<hex>\".`,\n      'malformed_header',\n    );\n  }\n\n  if (tolerance > 0 && Math.abs(now() / 1000 - timestamp) > tolerance) {\n    throw new GpmPayWebhookSignatureError(\n      `Webhook timestamp is outside the ${String(tolerance)}s tolerance (t=${String(timestamp)}). ` +\n        'Check for clock skew between your server and GPM Pay.',\n      'timestamp_skew',\n    );\n  }\n\n  // Hash over bytes, not characters: Vietnamese transfer contents are\n  // multi-byte, and hashing the string length would silently diverge.\n  const raw = toBuffer(input.rawBody);\n  const expected = createHmac('sha256', secret)\n    .update(Buffer.concat([Buffer.from(`${String(timestamp)}.`, 'utf8'), raw]))\n    .digest();\n\n  const provided = Buffer.from(v1, 'hex');\n  if (\n    expected.length !== provided.length ||\n    !timingSafeEqual(expected, provided)\n  ) {\n    throw new GpmPayWebhookSignatureError(\n      'Webhook signature mismatch — wrong secret, or the body was modified in transit. ' +\n        'Make sure you are verifying the raw request body, not a re-serialized object.',\n      'mismatch',\n    );\n  }\n\n  return { timestamp };\n}\n\n/** Boolean form of {@link assertWebhookSignature}. */\nexport function verifyWebhookSignature(input: VerifyWebhookInput): boolean {\n  try {\n    assertWebhookSignature(input);\n    return true;\n  } catch {\n    return false;\n  }\n}\n\n/**\n * Produce a signature header. Useful for testing your own handler and for\n * generating fixtures; GPM Pay signs real deliveries itself.\n */\nexport function signWebhookPayload(params: {\n  rawBody: string | Buffer | Uint8Array;\n  secret: string;\n  /** Unix seconds. Defaults to now. */\n  timestamp?: number;\n}): string {\n  const timestamp = params.timestamp ?? Math.floor(Date.now() / 1000);\n  const raw = toBuffer(params.rawBody);\n  const digest = createHmac('sha256', params.secret)\n    .update(Buffer.concat([Buffer.from(`${String(timestamp)}.`, 'utf8'), raw]))\n    .digest('hex');\n  return `t=${String(timestamp)},v1=${digest}`;\n}\n\nexport interface GpmPayWebhookEvent {\n  /** From `X-GPMPay-Event`; defaults to `transaction.created`. */\n  type: string;\n  /** Unix seconds the delivery was signed at. */\n  timestamp: number;\n  payload: WebhookPayload;\n  /** The raw body, for logging or re-verification. */\n  rawBody: string;\n}\n\nfunction headerValue(\n  headers: Record<string, string | string[] | undefined> | undefined,\n  name: string,\n): string | undefined {\n  if (!headers) return undefined;\n  const target = name.toLowerCase();\n  for (const [key, value] of Object.entries(headers)) {\n    if (key.toLowerCase() !== target) continue;\n    return Array.isArray(value) ? value[0] : value;\n  }\n  return undefined;\n}\n\n/**\n * Verify a delivery and parse it into a typed event.\n *\n * @throws {GpmPayWebhookSignatureError} when verification fails.\n */\nexport function constructWebhookEvent(\n  input: VerifyWebhookInput & {\n    headers?: Record<string, string | string[] | undefined>;\n  },\n): GpmPayWebhookEvent {\n  const { timestamp } = assertWebhookSignature(input);\n  const rawBody = toBuffer(input.rawBody).toString('utf8');\n\n  return {\n    type: headerValue(input.headers, EVENT_HEADER) ?? 'transaction.created',\n    timestamp,\n    payload: JSON.parse(rawBody) as WebhookPayload,\n    rawBody,\n  };\n}\n\n/**\n * Constant-time comparison for webhooks configured with\n * `authorizationType: 'API_KEY'`, where the raw secret is sent in a header\n * instead of being used to sign the body.\n */\nexport function verifyApiKeyHeader(\n  received: string | undefined,\n  expected: string,\n): boolean {\n  if (typeof received !== 'string' || expected === '') return false;\n  const a = Buffer.from(received, 'utf8');\n  const b = Buffer.from(expected, 'utf8');\n  if (a.length !== b.length) return false;\n  return timingSafeEqual(a, b);\n}\n","import { GpmPayConfigError, GpmPayWebhookSignatureError } from '../core/errors.js';\nimport {\n  SIGNATURE_HEADER,\n  constructWebhookEvent,\n  type GpmPayWebhookEvent,\n} from './verify.js';\n\n/**\n * Structurally-typed request. Deliberately not `express.Request` — this package\n * has zero dependencies and must not force Express into your tree.\n */\nexport interface MinimalRequest {\n  headers: Record<string, string | string[] | undefined>;\n  body?: unknown;\n  rawBody?: unknown;\n}\n\nexport interface MinimalResponse {\n  status(code: number): MinimalResponse;\n  json(body: unknown): unknown;\n  send(body?: unknown): unknown;\n}\n\nexport type NextFunction = (error?: unknown) => void;\n\nexport interface GpmpayWebhookOptions {\n  /** The webhook setting's `authorizationSecret`. */\n  secret: string;\n  onEvent: (\n    event: GpmPayWebhookEvent,\n    req: MinimalRequest,\n  ) => void | Promise<void>;\n  toleranceSeconds?: number;\n  /** Defaults to `X-GPMPay-Signature`. */\n  headerName?: string;\n  onError?: (error: GpmPayWebhookSignatureError, req: MinimalRequest) => void;\n  /**\n   * Respond 200 before awaiting `onEvent`. Default true.\n   *\n   * GPM Pay aborts a delivery after 5s and retries on the schedule\n   * `[10s, 30s, 2m, 10m, 1h, 6h]`, so a slow handler causes duplicate\n   * processing. Acknowledge fast, work afterwards.\n   */\n  respondEarly?: boolean;\n}\n\nconst BODY_PARSER_HINT =\n  'The GPM Pay webhook handler received an already-parsed body.\\n' +\n  'HMAC verification runs over the exact bytes GPM Pay sent, and re-serializing a\\n' +\n  'parsed object changes them — verification will ALWAYS fail.\\n\\n' +\n  'Mount the raw body parser on this route only:\\n' +\n  \"  app.post('/webhooks/gpmpay',\\n\" +\n  \"    express.raw({ type: 'application/json' }),\\n\" +\n  '    gpmpayWebhook({ secret, onEvent }));\\n\\n' +\n  'If a global express.json() already ran, capture the raw body with its verify hook:\\n' +\n  '  app.use(express.json({ verify: (req, _res, buf) => { req.rawBody = buf; } }));';\n\nfunction resolveRawBody(req: MinimalRequest): Buffer | string {\n  if (Buffer.isBuffer(req.rawBody)) return req.rawBody;\n  if (typeof req.rawBody === 'string') return req.rawBody;\n  if (Buffer.isBuffer(req.body)) return req.body;\n  if (typeof req.body === 'string') return req.body;\n\n  throw new GpmPayConfigError(BODY_PARSER_HINT);\n}\n\n/**\n * Express-compatible middleware that verifies the signature and hands you a\n * typed event.\n *\n * @example\n * app.post(\n *   '/webhooks/gpmpay',\n *   express.raw({ type: 'application/json' }),\n *   gpmpayWebhook({\n *     secret: process.env.GPMPAY_WEBHOOK_SECRET!,\n *     onEvent: async (event) => {\n *       if (event.payload.transferType !== 'in') return;\n *       const code = /DH(\\d+)/.exec(event.payload.content)?.[0];\n *       if (code) await fulfil(code, event.payload.transferAmount);\n *     },\n *   }),\n * );\n */\nexport function gpmpayWebhook(options: GpmpayWebhookOptions) {\n  const {\n    secret,\n    onEvent,\n    toleranceSeconds,\n    headerName = SIGNATURE_HEADER,\n    onError,\n    respondEarly = true,\n  } = options;\n\n  return async function handler(\n    req: MinimalRequest,\n    res: MinimalResponse,\n    next: NextFunction,\n  ): Promise<void> {\n    let event: GpmPayWebhookEvent;\n\n    try {\n      const rawBody = resolveRawBody(req);\n      const headerKey = Object.keys(req.headers).find(\n        (key) => key.toLowerCase() === headerName.toLowerCase(),\n      );\n      const rawHeader = headerKey === undefined ? undefined : req.headers[headerKey];\n      const signature = Array.isArray(rawHeader) ? rawHeader[0] : rawHeader;\n\n      event = constructWebhookEvent({\n        rawBody,\n        signature: signature ?? '',\n        secret,\n        headers: req.headers,\n        ...(toleranceSeconds === undefined ? {} : { toleranceSeconds }),\n      });\n    } catch (error) {\n      if (error instanceof GpmPayWebhookSignatureError) {\n        onError?.(error, req);\n        res.status(401).json({ error: error.message, reason: error.reason });\n        return;\n      }\n      next(error);\n      return;\n    }\n\n    if (respondEarly) {\n      res.status(200).json({ received: true });\n      try {\n        await onEvent(event, req);\n      } catch (error) {\n        next(error);\n      }\n      return;\n    }\n\n    try {\n      await onEvent(event, req);\n      res.status(200).json({ received: true });\n    } catch (error) {\n      next(error);\n    }\n  };\n}\n","import { GpmPayWebhookSignatureError } from '../core/errors.js';\nimport {\n  SIGNATURE_HEADER,\n  constructWebhookEvent,\n  type GpmPayWebhookEvent,\n} from './verify.js';\n\nexport interface NextVerifyOptions {\n  secret: string;\n  toleranceSeconds?: number;\n  /** Defaults to `X-GPMPay-Signature`. */\n  headerName?: string;\n}\n\n/**\n * Verify a webhook in a Next.js App Router route handler.\n *\n * `await request.text()` yields the exact bytes GPM Pay sent, so no special\n * body-parser configuration is needed.\n *\n * @throws {GpmPayWebhookSignatureError}\n */\nexport async function verifyNextRequest(\n  request: Request,\n  options: NextVerifyOptions,\n): Promise<GpmPayWebhookEvent> {\n  const { secret, toleranceSeconds, headerName = SIGNATURE_HEADER } = options;\n\n  const rawBody = await request.text();\n  const headers: Record<string, string> = {};\n  request.headers.forEach((value, key) => {\n    headers[key] = value;\n  });\n\n  return constructWebhookEvent({\n    rawBody,\n    signature: request.headers.get(headerName) ?? '',\n    secret,\n    headers,\n    ...(toleranceSeconds === undefined ? {} : { toleranceSeconds }),\n  });\n}\n\n/**\n * A ready-made App Router POST handler.\n *\n * @example\n * // app/api/webhooks/gpmpay/route.ts\n * import { createNextWebhookHandler } from '@gpmpay/sdk/webhooks';\n *\n * export const POST = createNextWebhookHandler({\n *   secret: process.env.GPMPAY_WEBHOOK_SECRET!,\n *   onEvent: async (event) => {\n *     if (event.payload.transferType !== 'in') return;\n *     const code = /DH(\\d+)/.exec(event.payload.content)?.[0];\n *     if (code) await fulfil(code, event.payload.transferAmount);\n *   },\n * });\n */\nexport function createNextWebhookHandler(\n  options: NextVerifyOptions & {\n    onEvent: (event: GpmPayWebhookEvent) => void | Promise<void>;\n  },\n): (request: Request) => Promise<Response> {\n  const { onEvent, ...verifyOptions } = options;\n\n  return async function POST(request: Request): Promise<Response> {\n    let event: GpmPayWebhookEvent;\n    try {\n      event = await verifyNextRequest(request, verifyOptions);\n    } catch (error) {\n      if (error instanceof GpmPayWebhookSignatureError) {\n        return Response.json(\n          { error: error.message, reason: error.reason },\n          { status: 401 },\n        );\n      }\n      throw error;\n    }\n\n    await onEvent(event);\n    return Response.json({ received: true });\n  };\n}\n\n/**\n * Read a raw body from a stream, for the Pages Router.\n *\n * Requires disabling the built-in parser:\n * ```ts\n * export const config = { api: { bodyParser: false } };\n * ```\n *\n * @param maxBytes Guard against unbounded bodies. Default 1 MiB.\n */\nexport async function readRawBody(\n  stream: AsyncIterable<Uint8Array>,\n  maxBytes = 1024 * 1024,\n): Promise<Buffer> {\n  const chunks: Buffer[] = [];\n  let total = 0;\n\n  for await (const chunk of stream) {\n    const buf = Buffer.from(chunk);\n    total += buf.length;\n    if (total > maxBytes) {\n      throw new Error(\n        `Webhook body exceeded ${String(maxBytes)} bytes; refusing to buffer more.`,\n      );\n    }\n    chunks.push(buf);\n  }\n\n  return Buffer.concat(chunks);\n}\n","/**\n * String-literal unions mirroring the backend's Prisma enums and\n * `apps/backend/src/modules/api-tokens/api-token.types.ts`.\n *\n * These are deliberately vendored rather than imported from `@repo/shared-types`\n * (that package is unpublishable). `src/types/__compat__.ts` asserts they stay\n * in sync at compile time.\n */\n\n/** Scopes an API token can carry. */\nexport type ApiTokenScope =\n  | 'transactions:read'\n  | 'bank-accounts:read'\n  | 'webhooks:manage';\n\nexport const ALL_API_SCOPES: readonly ApiTokenScope[] = [\n  'transactions:read',\n  'bank-accounts:read',\n  'webhooks:manage',\n] as const;\n\n/** `ACTIVE` = usable. `PENDING` = temporarily blocked by the owner. */\nexport type ApiTokenStatus = 'ACTIVE' | 'PENDING';\n\nexport type TransactionType = 'IN' | 'OUT';\n\nexport type TransactionSource = 'REAL' | 'SIMULATED';\n\nexport type BankAccountStatus =\n  | 'ACTIVE'\n  | 'SUSPENDED'\n  | 'DELETED'\n  | 'PENDING_VERIFICATION';\n\nexport type WebhookDriver = 'HTTP' | 'TELEGRAM' | 'WORDPRESS' | 'GOOGLE_SHEETS';\n\nexport type WebhookAuthType = 'NONE' | 'API_KEY' | 'HMAC';\n\nexport type WebhookScope = 'ALL' | 'SPECIFIC';\n\nexport type WebhookDeliveryStatus =\n  | 'PENDING'\n  | 'DELIVERED'\n  | 'RETRYING'\n  | 'FAILED';\n\n/**\n * Delivery retry backoff used by the GPM Pay webhook dispatcher, in seconds.\n * Mirrors `apps/backend/src/modules/webhook-delivery/backoff.util.ts`.\n *\n * Your endpoint will be called up to {@link WEBHOOK_MAX_ATTEMPTS} times for the\n * same transaction — make it idempotent on `payload.id`.\n */\nexport const WEBHOOK_RETRY_SCHEDULE_SECONDS: readonly number[] = [\n  10, 30, 120, 600, 3600, 21600,\n] as const;\n\nexport const WEBHOOK_MAX_ATTEMPTS = 6;\n\n/**\n * The HTTP delivery driver aborts after this long. Respond within it or the\n * delivery is recorded as failed and retried.\n */\nexport const WEBHOOK_DELIVERY_TIMEOUT_MS = 5000;\n"]}