DAFpay Payment Handler — com.dafpay¶
DAFpay is a payment handler: a "Pay with DAF" checkout method where a buyer
settles a transaction by authorizing a grant from a donor-advised fund, much as
Apple Pay or PayPal appear as wallet methods at checkout. It is declared as a
com.dafpay entry in a UCP payment_handlers registry and is
validated by
source/schemas/payments/dafpay.json
($id: https://ugp.dev/schemas/payments/dafpay.json), whose config $def is
carried as the handler entry's config. The DAF instruments the handler exposes
are validated by the base
payment_handler.json available_instruments
field, which resolves to
shopping/types/available_payment_instrument.json.
Relationship to UGP¶
UGP's donations capability moves money over bank rails — a platform remits
funds it already holds to a bank destination, so UGP nonprofit profiles keep
payment_handlers empty. DAFpay is the opposite case: a buyer who has not yet
moved money chooses, at checkout, to pay from their DAF. That is exactly the UCP
payment_handler primitive — a tokenized payment instrument processed at purchase
time. DAFpay therefore lives under the Chariot-owned com.dafpay namespace as a
UCP payment handler, not under dev.ugp.*, and is independent of the
bank-rail donation-batch workflow described in Donations. A
profile may advertise DAFpay alongside (or instead of) the giving capabilities.
Declaration¶
The handler is declared in a profile's payment_handlers registry, keyed by
com.dafpay:
jsonc
"payment_handlers": {
"com.dafpay": [
{
"version": "2026-06-25",
"id": "dafpay-default",
"spec": "https://ugp.dev/specification/dafpay",
"schema": "https://ugp.dev/schemas/payments/dafpay.json",
"available_instruments": [
{ "instrument": "com.dafpay.daf", "display_name": "Pay with DAF" }
],
"config": {
"flow": "redirect",
"authorization_url": "https://dafpay.com/authorize",
"capture": "automatic",
"settlement": { "mode": "guaranteed", "expected_clearing_days": 3 },
"supported_currencies": ["USD"],
"fees": { "percentage": 0, "fixed": 0, "paid_by": "merchant" },
"available_instruments": [
{
"instrument": "com.dafpay.daf",
"display_name": "Pay with DAF",
"min_amount": 5000,
"supported_providers": ["Fidelity Charitable", "Schwab Charitable", "NPT"]
}
]
}
}
]
}
The complete example is available as
example-dafpay-handler.json. Note
available_instruments appears twice: at the handler level (required by the base
payment-handler schema, minItems: 1) for coarse discovery, and inside config
as the richly-typed DAFpay version carrying per-instrument constraints.
End-to-end flow¶
- Discover the handler. The buyer's platform fetches the merchant's
/.well-known/ucp, readspayment_handlers["com.dafpay"], and renders a "Pay with DAF" button fromavailable_instruments. Discovery is the only step that touches the UCP profile; the rest is the handler's own protocol. - Authorize the grant. On click, the buyer is sent to
authorization_url(forflow: "redirect") — or an embedded widget loads (flow: "embedded") — with the amount, currency, and the merchant return URL appended viareturn_url_param. The buyer selects a DAF sponsor and authorizes a grant. - Capture. With
capture: "automatic"the grant is captured on authorization; withmanualthe merchant captures it in a later call. A captured authorization yields a grant/transaction id the merchant records. - Settle. DAF grants clear asynchronously. With
settlement.mode: "guaranteed", DAFpay fronts the funds at authorization and the merchant is made whole immediately; withdeferred, the merchant is paid when the grant clears (typicallysettlement.expected_clearing_days). - Confirm. The buyer is returned to the merchant return URL with the authorization result; the merchant completes the order/receipt.
Settlement modes¶
authorize ──► capture ──► (guaranteed) merchant funded now ─────────────► clear
└──► (deferred) merchant funded on clear ─────────► clear
guaranteed— DAFpay assumes the timing/clearing risk; the merchant sees funds at authorization. Best for checkout parity with cards/wallets.deferred— the merchant is paid when the DAF sponsor disburses the grant, after roughlyexpected_clearing_days.
Config field reference¶
| Field | Type | Notes |
|---|---|---|
flow |
"redirect" | "embedded" |
Required. How the buyer authorizes the grant. |
authorization_url |
URI | Where the buyer authorizes the grant. Required for redirect. |
return_url_param |
string | Query param the merchant return URL is appended to. Default return_url. |
capture |
"automatic" | "manual" |
When the grant is captured. Default automatic. |
settlement.mode |
"guaranteed" | "deferred" |
Who bears clearing risk. Default guaranteed. |
settlement.expected_clearing_days |
integer ≥ 0 | Typical days for a grant to clear. |
supported_currencies |
string[] (≥1) | Required. ISO 4217 codes; USD today. |
fees.percentage |
number ≥ 0 | Percentage fee on the amount. |
fees.fixed |
amount (minor units) | Fixed per-transaction fee. |
fees.paid_by |
"merchant" | "donor" |
Who bears the fee. Default merchant. |
available_instruments[] |
daf_instrument[] (≥1) |
DAF instruments and per-instrument constraints. |
daf_instrument¶
| Field | Type | Notes |
|---|---|---|
instrument |
"com.dafpay.daf" |
Required. The DAF instrument identifier. |
display_name |
string | Checkout button label. Default Pay with DAF. |
supported_providers |
string[] | DAF sponsors the grant can route to; omit for any. |
min_amount / max_amount |
amount (minor units) | Per-grant floor/ceiling. |