Donation Mapping
This document details the real mapping between WeGive Transaction objects and Planning Center Donation records. Transactions sync exclusively through the once-daily batch job — there is no real-time transaction push (pushTransaction() is a no-op stub) — and each donation gets exactly one fund designation; split/multi-fund gifts aren’t supported.
Core Donation Mapping
Primary Fields
There’s no
currency, memo, or generic reference field mapping — this integration doesn’t track or send those. Currency is implicitly USD; there’s no multi-currency support.Batch Processing Integration
Daily Batch Organization
Real batch mechanics (confirmed againstGeneratePlanningCenterBatchesJob):
- One scheduled job, once per day at 8:01 AM, per enabled integration
- Selects WeGive transactions by
succeeded_atfalling within the previous calendar day (in the organization’s own timezone), withstatus = success,amount > 0,integration_enabled = 1, and noplanning_center_idyet - Every run also does a second “missing” catch-up pass with no date bound, covering any transaction that still lacks a
planning_center_idfor any reason - Batch name format:
WeGive Import MM/DD/YYYY - A day with zero eligible transactions logs the batch as
ignored— no Planning Center batch is created for it at all
- The batch is looked up by matching
descriptionin Planning Center, or created if not found - After all donations in the run are processed, the batch is committed automatically if its status is
in_progress— there’s no manual/uncommitted state exposed anywhere
Batch Rollback (Error Handling)
If anything increateTransactionsBatch() throws (outside the per-transaction catch described below — e.g. the initial batch/payment-source lookup fails), the entire run is rolled back:
- Any
planning_center_ids set on transactions during that run are cleared back tonull - The partial batch itself is deleted in Planning Center
Fund Designation Mapping
Single Fund Designation (the only kind supported)
Each donation includes exactly oneDesignation object, resolved in this fixed priority order:
- The transaction’s own linked fund, if it has a
planning_center_id - The integration’s configured
default_fund_id - The destination organization’s oldest fund that has a
planning_center_id - If none resolve: the transaction throws
Unknown Fund Sourceand is skipped (not the whole batch)
There is no split/multi-fund designation support in this integration — a WeGive transaction with multiple fund allocations still produces exactly one Planning Center Designation, tied to whichever fund resolves from the priority order above. The full transaction amount goes to that one fund.
Payment Source Management
WeGive Payment Source
- A single Planning Center payment source named “WeGive” is found by name or created automatically, once per batch run, and reused for every donation in that run
- The original payment method is preserved only via the donation’s own
payment_methodattribute (cardorach) — there’s no separate metadata field storing card type, last-4, bank name, or check number
There is no per-payment-method breakdown (“Bank/Check/Cash” mapping) — every WeGive source type other than card resolves to
ach in the payment_method field sent to Planning Center. Check and cash transactions aren’t distinguished.Data Transformation
Donation Creation Processing (Push, via daily batch)
- Find-or-create the day’s batch and the “WeGive” payment source (once per run)
- For each eligible transaction: push the donor (if not already linked) and the fund (if not already linked)
- Resolve the fund via the priority order above; skip (catch + report to Sentry) if none resolves
- POST the donation with
amount_cents,payment_method,payment_source_id,person_id,received_at, plus one nestedDesignation - Store the returned donation ID as
planning_center_idon the transaction - Commit the batch once all transactions are processed
Pull Operation (Planning Center → WeGive)
- Page through
giving/v2/donations(100 per page), includingdesignations - For each donation, resolve its fund from the included
designations/fundrelationship (first designation only) - Match the donor by
planning_center_id; throw'No owner found'if none exists (this transaction is skipped, not created with a null owner) - Match the fund by
planning_center_idif present - Match a scheduled donation by the pulled donation’s
recurring_donationrelationship, if present (read-only correlation — see below) - Guard against CRM-owned-field overwrite: if the local transaction already has a
correlation_id(it was actually processed by WeGive) or is still future-dated-pending, itsamountand fund allocations are preserved as-is — a Planning Center-side edit does not overwrite the real payment data. Otherwise,amountand (if fund allocations are enabled for the org) a single fund allocation are rebuilt from the pulled data.
Recurring Donation Integration
There is no independent recurring-gift sync in this integration. A pulled donation’s relationship to a Planning Center recurring donation is read and used only to link the resulting WeGive Transaction to an existing
ScheduledDonation record by planning_center_id — WeGive never creates, updates, or pushes a recurring-gift schedule to Planning Center. If no matching ScheduledDonation exists locally, the link is simply not made; nothing fails because of it.Validation and Error Handling
Donation Validation
- Non-positive transaction amounts are silently skipped on push (Planning Center’s API requires positive amounts) — not an error, not logged
- A transaction whose fund can’t resolve throws
Unknown Fund Source, caught per-transaction and reported to Sentry — the transaction stays unlinked (noplanning_center_id) and is re-attempted on the next run - A pulled donation with no matching donor throws
'No owner found'— that donation import fails; there’s no “create the donor from the donation” fallback
Batch Error Handling
- A single transaction’s push failure (thrown inside the per-transaction try/catch) is caught, reported to Sentry, and the loop continues — it does not fail the whole batch
- A failure in batch-level setup (the batch lookup, the payment-source lookup/creation) is not caught per-transaction — it propagates up and triggers the full run-level rollback described above
Performance Considerations
- All transaction sync happens through the once-daily batch job — there’s no way to force an immediate/real-time transaction push
- API calls are throttled to Planning Center’s real limit (70 requests / 20 seconds) via a hardcoded internal counter
- GET requests (fund/payment-source lookups, donation/fund pulls) are retried up to 3 times on transient failures; the mutating POST/PATCH/DELETE calls that create the actual records are not automatically retried on failure
Known Gaps (not implemented)
- Real-time/immediate transaction push
- Split/multi-fund designations per donation
- Currency field beyond implicit USD
- Memo/reference field sync
- Distinct payment-method categories beyond card/ach
- Independent recurring-gift creation or schedule sync