Skip to documentation content

QuickBooks to Stripe

Link QuickBooks invoice facts to Stripe preparation, execute the payment in your backend, and retain a stable evidence record.

On this page

Use QuickBooks and Stripe together

QuickBooks Online is the invoice source; Stripe is the payment integration. These roles are additive, not alternatives. Carden imports and validates accounting facts, your merchant server requests prepared fields, and your existing backend sends those fields to Stripe. The SDK does not ask Stripe to pull an invoice, and Carden does not execute your payment.

ModeInvoice inputStripe-key scopes
Linked QuickBooks sourceReference one imported invoice and its current revision.invoices:read + enrichment:write; add payments:write to report outcomes.
Inline invoiceSend your own complete CanonicalInvoice v1 to the original enrichment endpoint.enrichment:write; add payments:write to report outcomes.
Reporting onlyReport actual payment observations without requesting enrichment.payments:write; omit enrichmentRequestId when no preparation exists.

Link the source and explicitly grant access

  1. Confirm the same merchant and environment for QuickBooks and Stripe. Authorize the intended QuickBooks company if it is not already connected.
  2. Complete the bounded import and review invoice, Item, and Preferences records. Resolve factual mapping exceptions with an authorized merchant reviewer.
  3. Link the QuickBooks integration as the invoice source for the Stripe integration. The caller must have authority over both the source and destination; a connection ID alone grants no access.
  4. Create a Stripe-integration Carden key and explicitly opt into invoices:read. Include enrichment:write for preparation and payments:write if this server reports payments.
  5. Deploy the key and Carden origin in your server-only secret configuration. A separate QuickBooks management key is needed only for invitation or sync operations.

Existing keys gain no automatic invoice-read grant. Adding the source link does not upgrade old keys, transfer authority from another environment, or grant broader provider-management scopes. Issuing the new scope requires authority over both the linked source and destination. Review access and rotate the service key deliberately.

In Add integrations, select both QuickBooks and Stripe when both roles are needed; this adds Carden integration configuration, not new provider accounts. In the Stripe editor, use Invoice data source to link the connected QuickBooks integration in the same environment. The caller needs integrations:admin on both integrations and invoices:read on the source; key issuance also checks source integrations:read.

The invoice Preview control inspects source readiness, issues, mapping versions, and candidate Stripe fields only. It does not execute a payment or save a reportable preparation. Request a persisted preparation through the from-invoice API before sending a payment, and use its preparation.id rather than an invoice UUID, snapshot UUID, or preview request ID in reports.

Prepare only a supported full invoice

Look up the stored invoice by its actual QuickBooks invoice ID or exact invoice number, then retrieve its detail. Carden's invoice UUID is a different identifier from the QuickBooks ID. Inspect ready, issues, revision, checkedThrough, and source provenance. Pass expectedRevision from that lookup to detect a source or mapping change before preparation.

  • The invoice must be open, unpaid, and positive. Its balance must equal its full total; the requested amount and lowercase currency must match exactly.
  • The linked source must be active and fresh. A paused or disconnected source, pending refresh, missing dependencies, or stale state blocks new preparation.
  • A missing or ambiguous lookup is an exception, not permission to pick the first record. Reconcile the actual invoice identity before continuing.
  • Partially paid invoices, partial allocations, overpayments, multiple-invoice payments, and unsupported source representations are outside this stored-invoice preparation flow.
  • Preparation checks eligibility again. A previous ready lookup is not a reservation and does not ensure the source stayed unchanged.

Retain representation and provenance

QuickBooks transaction-level tax is supported without pretending that item-level tax is available. When tax or discounts are not allocated by the source, line taxAmountMinor, discountAmountMinor, and totalAmountMinor remain null. Invoice-level totals remain authoritative. This stored representation does not relax or change the original inline CanonicalInvoice v1 contract.

A preparation preserves an immutable invoice snapshot, the exact QuickBooks Invoice, Item, and Preferences source-record references, the mapper version, and the approved mapping values and versions used. Retain the source connection as well as the integration identity; later connections or records must not silently replace historical evidence.

After a source or mapping correction, reprocess only trusted current-source failures. A mapping update is not a reason to reactivate a deleted source record, an ambiguous match, or an invoice from an old or replaced source connection. Verify the current source and inspect a fresh revision before trying again.

Look up, prepare, execute once, and report

Use the supplied private @carden/node workspace package on your merchant server. The example calls the real SDK methods; order, attemptStore, executeStripePayment, and reportDeliveryMonitor are application-owned dependencies, not additional Carden endpoints or SDK methods. Persist the attempt and both idempotency keys before running it.

Server-side QuickBooks-to-Stripe flow
import { paymentObservationFromStripePaymentIntent } from "@carden/node";

// Merchant-owned prerequisites, not extra Carden SDK methods:
// - carden: a configured server client with invoices:read, enrichment:write,
//   and payments:write for Stripe and its explicitly linked QuickBooks source.
// - order: the actual QuickBooks invoice ID, payment amount, and currency.
// - attempt: a durably stored eventId, attemptId, preparationIdempotencyKey,
//   stripeIdempotencyKey, and known capturedAmountMinor/refundedAmountMinor.
// - attemptStore: your durable attempt repository.
// - executeStripePayment: your existing merchant-authenticated Stripe operation,
//   returning a PaymentIntent with an expanded latest_charge for refund evidence.
// - reportDeliveryMonitor: safe operational alerting; durable recovery is separate.
// carden is the configured server-side client with an opted-in invoices:read key.
// order.quickbooksInvoiceId is the actual source ID stored with your order.
const page = await carden.invoices.list({
  quickbooksInvoiceId: order.quickbooksInvoiceId,
  limit: 20,
}, { timeoutMs: 5000 });
if (page.invoices.length !== 1 || page.nextCursor !== null) {
  throw new Error("Resolve the missing or ambiguous invoice before preparing payment data.");
}
const invoice = await carden.invoices.retrieve(page.invoices[0].id, { timeoutMs: 5000 });
if (!invoice.ready || !invoice.revision) {
  throw new Error("Review invoice issues and refresh the source before preparation.");
}
// Preparation rechecks access, freshness, revision, and payment eligibility.
// A ready lookup is not a reservation or a guarantee that preparation will succeed.

const { enrichment, preparation, requestId } =
  await carden.stripe.createPaymentIntentEnrichmentFromInvoice({
    invoiceId: invoice.id,
    amountMinor: order.amountMinor,
    currency: order.currency,
    expectedRevision: invoice.revision,
    idempotencyKey: attempt.preparationIdempotencyKey,
  }, { timeoutMs: 5000 });

// Persist before executing Stripe. requestId diagnoses this HTTP call;
// preparation.id is the stable correlation ID used for every later report.
await attemptStore.attachPreparation(attempt.attemptId, { preparation, requestId });

const result = await carden.stripe.trackPayment(
  () => executeStripePayment({
    amount: preparation.amountMinor,
    currency: preparation.currency,
    ...enrichment, // Only Stripe fields; never spread preparation metadata into Stripe.
  }, { idempotencyKey: attempt.stripeIdempotencyKey }),
  {
    report: {
      eventId: attempt.eventId,
      attemptId: attempt.attemptId,
      currency: preparation.currency,
      amountMinor: preparation.amountMinor,
      capturedAmountMinor: attempt.capturedAmountMinor,
      refundedAmountMinor: attempt.refundedAmountMinor,
      enrichmentRequestId: preparation.id,
      ...(invoice.invoiceNumber ? { invoiceReference: invoice.invoiceNumber } : {}),
    },
    selectResult: (intent) => paymentObservationFromStripePaymentIntent(intent),
    reportOptions: { maxRetries: 0, timeoutMs: 1000 },
    onReportError: (error, { report }) => {
      reportDeliveryMonitor.record({
        attemptId: attempt.attemptId,
        eventId: report?.eventId ?? attempt.eventId,
        errorType: error.name,
      });
    },
  },
);
// The wrapper invokes executeStripePayment once, preserving its result or error.
// A reporting error never retries the payment. Your durable outbox/reconciliation
// process recovers missing reports from the saved attempt and preparation.id.
// Later verified Stripe webhook observations reuse preparation.id, even if the
// QuickBooks invoice changed or its source was disconnected after preparation.

The amount and currency sent to Stripe are the amounts checked by preparation. Your existing Stripe operation remains responsible for customer and payment-method selection, supported API fields, confirmation, and payment idempotency. The wrapper invokes that operation once per invocation; your own durable attempt policy prevents duplicate execution across process restarts or repeated checkout requests.

Keep preparation identity separate from HTTP identity

ValuePurpose
idempotencyKey in preparation inputIdentifies the same preparation request across retries; persist it before the first request.
preparation.idStable saved preparation identity. Send it as enrichmentRequestId on reports for this source flow.
requestId / x-carden-request-idIdentifies one HTTP request for diagnostics; accepted retries can have different request IDs.
preparation.snapshotId and revisionIdentify the immutable invoice state and source/mapping revision used.
Stripe idempotency keyYour existing backend's payment-execution identity; preparation does not implement Stripe idempotency.
attemptId / eventIdOne payment attempt / one immutable observation; a delivery retry retains both.

An accepted identical preparation retry retains preparation.id and the prepared snapshot while current source checks still pass; idempotency does not bypass freshness or a changed source, link, connection, or mapping revision. Changing the invoice selector, amount, currency, or expected revision under the same idempotency key conflicts. Review a changed invoice and payment state before deliberately starting new preparation. Keep the original preparation for historical reports rather than replacing it.

Report real outcomes even after source changes

Report the actual payment or later verified provider observation using the saved preparation.id. Reporting does not depend on the QuickBooks invoice remaining current, connected, or unchanged after preparation. The reporting key must still be authorized for the same Stripe integration and environment.

If the reported amount or currency does not match a stored preparation, Carden retains the actual observation and flags a correlation exception rather than treating it as successful enrichment. Your backend must also verify the invoice reference; the current automated checks compare amount and currency. Never change the report's financial facts to make it agree with a previous preparation.