End-to-end deposit walkthrough โ ProMount by Vancomm entity. Follow phases 1โ8 in order, check each box as you go.
Before you start โ confirm all of these are live. The automated steps in Phases 4โ6 will not fire unless every prerequisite is wired. None of these need re-checking between test runs; they are one-time setup items. The deployed bridge Worker is scoro-qbo-bridge.vancomm.workers.dev.
โก Run this first โ green means ready. The prerequisites below can be auto-checked in one command:
It reports PASS / FAIL / MANUAL for each prerequisite. It is read-only โ it checks state and creates nothing. The list below is the reference for what each item checks and how to fix it if the preflight flags something.
scoro-qbo-bridge.vancomm.workers.dev/webhooks/scoro/health returns {"ok":true}scoro-qbo-bridge.vancomm.workers.dev/webhooks/scoro. Wire it in Scoro โ Settings โ Triggers and actions. Or subscribe via the API instead of the UI:
/webhooks/scoro, also configured in Triggers and actions.payment.create) โ Worker target scoro-qbo-bridge.vancomm.workers.dev/webhooks/qbo. Set it up at the Intuit Developer dashboard (My Apps โ your app โ Webhooks). Copy the verifier token shown there, then set the Worker secret QBO_WEBHOOK_VERIFIER_TOKEN:
scoro-qbo-kv). If auth errors, mint a fresh refresh token and push it to the Worker. Recommended path โ Intuit's OAuth 2.0 Playground (browser, no local setup):
com.intuit.quickbooks.accounting scope.Note: Tailscale is not needed for the Worker โ it only ever mattered for the old local-callback OAuth script.
Alternative (local script): if you prefer running the local OAuth flow on your own machine, tailscale funnel 8742 then doppler run -p vancomm -c dev_vancomm -- node scripts/qbo-oauth.mjs, and push the resulting token with the same wrangler secret put QBO_REFRESH_TOKEN command above.
c_qbo_invoice_link (field_id 27) โ "Can add text in HTML format" MUST be ON. Check at Scoro โ Settings โ Custom fields.[a]c_qbo_invoice_link[/a] in the Payment Instructions block. Check at Scoro โ Settings โ PDF templates.Test amounts: keep totals small ($5โ$10). Real money flows through QBO Payments. Small amounts limit exposure during validation. Deposit formula: deposit = quote sum ร prepayment_percent (e.g. $1.00 ร 50% = $0.50).
invoices/prepayments/view / invoices/prepayments/modify) but does not create them.
c_qbo_invoice_link field is empty at this point.
/webhooks/scoro):
/webhooks/scorosum + prepayment_percentInvoiceLink (hosted pay page URL)<a href="[pay URL]">Pay online</a> into c_qbo_invoice_link (field_id 27) on the Scoro prepaymentc_qbo_invoice_link may still be empty. Refresh the Scoro prepayment record after ~60 seconds before sending.
c_qbo_invoice_link (field_id 27) must have "Can add text in HTML format" = ON โ check at Settings โ Custom fields (Prerequisite P-8). If that flag is off, the PDF shows <a href=...> as text instead of a link. Prepayment PDFs use template 51.
c_qbo_invoice_link field shows a value (not empty)
/webhooks/qbo):
payment.create webhook โ Worker /webhooks/qboQBO_WEBHOOK_VERIFIER_TOKENqbomap:qbo_invoice:{id} in KV โ resolves to Scoro prepayment IDreceipts/modify with prepayment_id set) โ Scoro prepayment flips to paid/reconciled/webhooks/scoroinvoices/prepayments/listInvoiceLink and writes it to c_qbo_invoice_link on the Scoro final invoicec_qbo_invoice_link is populated; final invoice PDF (template 49) shows a clickable Pay online link (same Caveats A & B as Phase 4)
c_qbo_invoice_link field) and pay the remaining balance. Alternatively, record the payment directly in QBO against the balance invoice.
c_qbo_invoice_link is populated on both the prepayment and the final invoice| Item | Expected |
|---|---|
| Order total (T) | [your test amount] |
| Deposit paid (D) | T ร 50% |
| Balance paid (B) | T โ D = T ร 50% |
| QBO deposit invoice | = D |
| QBO balance invoice | = B |
| D + B | = T โ |
| Symptom | Most likely cause | Action |
|---|---|---|
| Pay-link blank on prepayment PDF (Phase 4) | Worker Path A hasn't fired yet, or fired with an error | Wait 60s; refresh the Scoro prepayment. If still blank, check the Worker log viewer |
Pay-link shows literal HTML (<a href=...>) |
"Can add text in HTML format" flag is OFF on c_qbo_invoice_link |
Scoro โ Settings โ Custom fields โ c_qbo_invoice_link โ enable the flag โ re-run the Worker for that prepayment via /process/prepayment/{id} |
| Scoro prepayment does not flip to paid after deposit payment (Phase 5) | QBO Payments webhook not wired, QBO_WEBHOOK_VERIFIER_TOKEN mismatch, or no qbomap KV entry (Path A failed) |
Confirm P-4; check Worker logs; if KV entry missing, re-drive Path A first |
| "customer mapping unresolved" in Worker logs | Scoro company โ QBO customer mapping (T-031) not seeded for this customer | Seed the mapping in Worker KV: qbomap:scoro_company:{companyId} โ {customerId, billEmail} |
| Webhook did not fire (no entry in Worker logs) | Scoro trigger not wired, or Worker URL is wrong | Scoro โ Settings โ Triggers and actions โ verify the trigger exists and points to scoro-qbo-bridge.vancomm.workers.dev/webhooks/scoro |
| Deposit amount incorrect | prepayment_percent incorrect on the Scoro prepayment |
Open the prepayment โ confirm advance percentage is 50 (not 100). If reset, the invoices/prepayments/modify call may have omitted prepayment_percent โ check Worker Path A code |
| Final invoice pay-link blank (Phase 6) | Worker Path C failed to get the QBO InvoiceLink |
Check Worker logs for pathC; confirm QBO Payments is enabled; re-trigger via /process/order-complete/{orderId} |
| QBO balance invoice exists but Scoro invoice not marked paid (Phase 7) | qbomap KV entry for the balance invoice was not written by Path C |
Check that Path C completed fully; confirm KV key qbomap:qbo_invoice:{balanceInvoiceId} exists |
| Duplicate receipts in Scoro | Both the native connector (P-7) and Worker Path B fired for the same payment | Not harmful โ Scoro deduplicates by amount/date. If a double-credit occurs, void the duplicate receipt manually in Scoro UI |
QBO refresh tokens expire after 100 days.
If the Worker returns token-related errors, re-run the OAuth flow: tailscale funnel 8742 && doppler run -- node scripts/qbo-oauth.mjs โ the rotated token is persisted automatically in Worker KV on every successful API call.
Rollback
See deliverables/runbooks/qbo-e2e-quote-to-cash/README.md โ the rollback section lists --delete script commands for all QBO artifacts. Scoro receipts have no API delete endpoint; void them manually in Scoro UI.
Related