Skip to content

Data Model Reference

Payment Method#

Stores a donor's saved payment method as a reference to a tokenized record in the payment gateway (e.g. a Stripe PaymentMethod/Customer ID), so the donor can reuse it for future one-time gifts or recurring installments without re-entering card details. Only non-sensitive display data (card brand, last 4 digits, expiry month/year) and gateway reference IDs are stored — the platform never stores the full card number, CVV, or bank account number.

custom · 16 fields · ReadWrite · Donations & Payments

Why it exists#

This is a pure token vault, never a payment record. Stripe.js tokenizes card details in the donor's browser and only paymentMethod.id ever reaches Apex — the full card number, CVV, and bank account number never touch this org, so Payment_Method__c only ever stores what's safe to display back to the donor (Brand__c, Last4__c, expiry) plus the gateway's own reference IDs. Fingerprint__c lets the app detect when a donor re-saves a card it already has on file.

This object does not relate to Opportunity. A one-time gift stores its own Stripe payment reference directly on the Opportunity (Stripe_Payment_Intent_Id__c, Payment_Source__c, Payment_Status__c) and never touches a saved payment method. Payment_Method__c exists specifically so a recurring schedule (Recurring_Donation__c) can be charged off-session without the donor re-entering card details each cycle — see Recurring Donation.

Relationships#

Direction Field Related object Relationship type
→ out Contact__c Contact Lookup
→ out Donor_Account__c Account Lookup
→ out Payment_Account__c Payment Account Lookup
← in Payment_Method__c on Recurring Donation Recurring Donation Lookup

Fields#

Field Label Type Notes
Bank_Name__c Bank_Name Text Bank name shown for ACH payment methods. Not populated for cards.
Brand__c Brand Text Card network (Visa, Mastercard, Amex, Discover). Blank for ACH.
Contact__c Contact Lookup → Contact Owning contact in standard (non-Person Account) orgs.
Donor_Account__c Donor Account Lookup → Account Owning Person Account in Person Account orgs.
Exp_Month__c Exp_Month Number Card expiry month, for display only. Blank for ACH.
Exp_Year__c Exp_Year Number Card expiry year, for display only. Blank for ACH.
Fingerprint__c Fingerprint Text Gateway fingerprint used to detect duplicate payment methods.
Docs: PaymentMethodService.saveWalletCard matches on this before inserting; a hit returns the existing row untouched with no DML at all rather than a duplicate, since the guest context all 3 callers run in can never get object-level Edit on this object to update it.
Is_Default__c Is_Default Checkbox Checked if this is the donor's default payment method.
Is_Valid__c Is_Valid Checkbox Unchecked when the gateway reports this method as no longer usable.
Docs: PaymentMethodsSelector.selectByDonor filters WHERE Is_Valid__c = true so an invalid method never surfaces as a choice in the donor-facing picker, while selectByDonorSystem (used by the guest/portal write paths) reads all rows so Fingerprint__c dedup still finds them.
Last4__c Last4 Text Last 4 digits shown to identify the method. Never the full card/account number.
Payment_Account__c Payment Account Lookup → Payment_Account__c Which of your configured Stripe accounts holds this saved card. Set automatically when the donor saves the card and must not be changed by hand — the card only works with the account listed here.
Docs: Added 2026-08-07 to close a live renewal bug. Before this field the renewal batch re-derived the Stripe account from Campaign.Payment_Account__c on every run, so re-pointing a campaign at a second Stripe account made every in-flight schedule charge account-A tokens against account B; Stripe answers resource_missing and the batch fed that into the dunning cadence as though the donor's card had declined. deleteConstraint is deliberately omitted (SetNull), matching Opportunity.Payment_Account__c and Donation_Staging__c.Payment_Account__c: this is a historical transactional reference, not a config link. The hard block on deleting an in-use account lives on Campaign.Payment_Account__c (Restrict) plus SetupController.DeletePaymentAccount's friendly guard, and every wallet row is reachable through a campaign, so nothing is lost by not repeating it here.
Removed_Date__c Removed Date DateTime The date and time the donor removed this card themselves from their online account. Blank means the card is still available to them. Useful when a donor asks why a card stopped being offered, or when reconciling a gift that was charged shortly before the card was removed.
Docs: Companion to Removed__c — set in the same SYSTEM_MODE update so the two never disagree. Nothing queries on it; it exists so support can answer "when did this card disappear from my account?" without reading field history, which is not enabled on this object.
Removed__c Removed Checkbox Ticked when the donor removed this card themselves from their online account. The card stays on file so past gifts and any refunds still trace back to it, but it is no longer offered when someone gives again or when a recurring gift is set up. Do not tick or untick this yourself — ask the donor to remove or re-add the card from their donor portal instead.
Docs: A soft remove, for three reasons: the Guest License can never hold object Delete; historic Donation__c rows need Stripe_Payment_Method_Id__c to stay resolvable for refund traceability; and DonorPortalService.removeWalletCard refuses removal outright while an Active, Paused or Failed Recurring_Donation__c still points at the card, so a removed row can never be one a live schedule depends on (Failed blocks too — a dunning donor who removed the card would have nothing left to fix). Removal makes NO Stripe detach callout — the site-context guard added to the gateway refuses back-office Stripe calls from a site context by design, and the donor's click path must not carry a callout. PaymentMethodsSelector filters Removed__c = true out of the two wallet-listing methods (selectValidByDonor, selectValidByDonorSystem). selectByDonorSystem is deliberately NOT filtered — it is the dedup query, and Stripe_Payment_Method_Id__c is unique, so hiding a removed row there would make re-adding the same card die on the uniqueness constraint instead of reinstating it. selectById / selectDetailById stay unfiltered so back-office lookups still resolve historic rows.
Stripe_Customer_Id__c Stripe Customer ID Text Stripe Customer reference ID (cus_...). Not a secret.
Stripe_Payment_Method_Id__c Stripe Payment Method ID Text Stripe PaymentMethod reference ID (pm_...). Not a secret.
Docs: The billing-critical copy of this id actually lives on Recurring_Donation__c, stamped directly by the caller of PaymentMethodService.saveWalletCard — this wallet-row copy is display/dedup metadata only, which is what makes the insert-only (never-updated) row safe under the guest-context restriction.
Type__c Type Picklist Card or US Bank Account (ACH).

← Back to the diagram