§ Guide · Integration flow

End-to-end payment authorization walkthrough

This guide follows one payment authorization from subject linking to callback completion. IDs and payload fields are identical to the SDK and API reference pages so teams can compare approaches without rewriting integration logic.

scenario
business context
integrator: Stripe
customer: Ada Lovelace
subjectId: cus_123
contextKey: merchant:acct_live_001
paymentIntent: pi_123
amount: $84.00
externalRequestId: payment_auth_001
§ Step 0

Link the customer to Nuvouch once

Create connection session
node sdk
import {
  createNuvouchIntegratorClientFromEnv,
  isConnectionAlreadyLinkedResponse,
  renderConnectionSessionQrDataUrl,
} from "@nuvouch/integrator-sdk";

const client = createNuvouchIntegratorClientFromEnv();

const session = await client.createConnectionSession({
  subjectId: "cus_123",
  subjectLabel: "Ada Lovelace",
  contextKey: "merchant:acct_live_001",
  contextType: "merchant",
  contextLabel: "Stripe Live Account",
});

if (isConnectionAlreadyLinkedResponse(session)) {
  await markCustomerAsLinked(session.connection.id);
} else {
  const qrDataUrl = await renderConnectionSessionQrDataUrl(session, {
    width: 320,
    type: "image/png",
  });

  await showConnectionPrompt({
    qrDataUrl,
    shortCode: session.shortCode,
    expiresAt: session.expiresAt,
  });
}
§ Step 1

Create approval request

Create approval request
node sdk
const approval = await client.createApprovalRequest({
  targetSubject: { subjectId: "cus_123", contextKey: "merchant:acct_live_001" },
  externalRequestId: "payment_auth_001",
  title: "Approve payment",
  summary: "Stripe needs the customer to confirm a high-risk payment.",
  requestedFor: "Payment authorization",
  actor: {
    id: "payments_agent_01",
    name: "Stripe Payment Agent",
    subtitle: "Payments risk workflow",
    avatarLabel: "ST"
  },
  context: {
    kind: "digital-service",
    title: "Card payment",
    location: "Stripe",
    reason: "High-risk payment requires customer confirmation",
    expiresAt: "2026-04-21T18:00:00.000Z",
    referenceCode: "pi_123"
  },
  risk: {
    level: "high",
    summary: "Customer confirmation required before capture",
    checks: ["3DS fallback unavailable", "Risk score above threshold"]
  },
  actions: [
    { label: "Approve payment", value: "approve" },
    { label: "Deny payment", value: "deny" }
  ],
  amount: "$84.00"
});
§ Step 2

Read request state only when needed

Read current request state
node sdk
const current = await client.getApprovalRequestByExternalId("payment_auth_001");
if (current.approvalRequest.status === "pending") {
  // diagnostic or operator view only
}
§ Step 3

Process signed callback safely

Verify and process webhook
sdk helper
const rawBody = await req.text();
const valid = await verifyNuvouchWebhookSignatureFromEnv({
  rawBody,
  signatureHeader: req.headers.get("x-nuvouch-signature"),
});
if (!valid) return new Response("invalid signature", { status: 401 });

const deliveryId = req.headers.get("x-nuvouch-delivery");
if (deliveryId && await alreadyProcessed(deliveryId)) {
  return new Response("duplicate", { status: 200 });
}

const event = parseNuvouchWebhookJson(rawBody);
if (event.kind === "approval" && event.body.payload.externalRequestId === "payment_auth_001") {
  if (event.body.payload.status === "approved") {
    await capturePaymentIntent("pi_123");
  } else if (event.body.payload.status === "denied") {
    await markPaymentAuthorizationDenied("pi_123");
  }
}

if (deliveryId) await markProcessed(deliveryId);
return new Response("ok");
§ Step 4

Handle cancellation and retries

Cancel pending request
node sdk
await client.cancelApprovalRequest("req_2f5cd6dbb6784b9ab2f7");
  • If callbacks are temporarily failing, Nuvouch retries automatically with backoff.
  • Return 2xx only after persisting event and dedupe key.
  • Use external payment authorization IDs as your reconciliation join key.
§ Next

Related references