Skip to content

purchaseProduct

purchaseProduct(product: EntityRef!, gift: ProductPurchaseGiftInput, buyer: BuyerInput, discountCode: String, giftCardCode: String, paymentMethod: ID, savePaymentMethod: Boolean, taxCalculationId: String, forRegistration: Boolean, recurringConsentAccepted: Boolean, emailMarketingConsent: Boolean, emailMarketingConsentDisclosureVersion: ID, attribution: AttributionInput, idempotencyKey: String, fundingSource: CheckoutFundingSource, bankDebitMandate: ID): PurchaseProductResult!

Purchase a product (pass, membership, gift card, or physical product). Returns a checkout session if payment is required, or the purchase directly for free products.

Pass buyer when the caller is not signed in to perform guest checkout. Requires the business's guestCheckoutEnabled setting. Passes and memberships always require a signed-in user.

Arguments

productEntityRef!
discountCodeString

Optional discount code typed by the customer. Validated server-side against the product's business; an invalid or unmet code returns a per-field error rather than purchasing at full price.

giftCardCodeString

Optional gift-card code to apply against the product price. The card's remaining balance covers up to the post-discount line; any remainder is charged via Stripe.

paymentMethodID

Existing saved Stripe PaymentMethod to charge. Currently used for membership subscriptions so a signed-in buyer can subscribe without re-entering card details.

savePaymentMethodBoolean

When true for a signed-in one-shot card checkout, attach the newly-entered card to the buyer's Stripe Customer for future purchases. Ignored for guest checkout and memberships.

taxCalculationIdString

Stripe Tax calculation ID returned by a recent quoteTax query. When provided, the server reuses that calculation rather than computing a fresh one — guaranteeing the buyer is charged the amount they saw. Ignored when the seller has tax disabled.

forRegistrationBoolean

Set when this purchase exists only to fund a registration that's being booked in the same flow (the buyer picked "buy a pass" as the payment method on a session). For a single-use pass the spot consumes the only session immediately, so the standalone purchase-confirmation email is suppressed — the registration confirmation already covers it. Multi-use passes still send the purchase email since sessions remain.

recurringConsentAcceptedBoolean

Separate affirmative consent for recurring membership charges. Required when purchasing a membership; ignored for one-time products.

emailMarketingConsentBoolean

Email-marketing opt-in captured at checkout. true records an affirmative consent for this buyer against the product's business; only honored when the business has consent collection enabled. Sessions never sends marketing email itself — consent lets the business email the participant through its own provider.

emailMarketingConsentDisclosureVersionID

Version of the consent disclosure the buyer was shown. Frozen with the consent record so the proof reflects exactly what they agreed to.

attributionAttributionInput

Marketing attribution captured client-side. First-touch is stamped onto a newly created participant; last-touch onto the purchase. See registerForActivity.

idempotencyKeyString

Opaque client-generated idempotency token, stable for one checkout attempt (e.g. a single crypto.randomUUID() minted when the payment step mounts, NOT regenerated per click). When provided, it is used as the Stripe idempotencyKey on the saved-card off-session charge so a double-submit / client retry deduplicates to a single live charge instead of charging twice. Omit it to keep the legacy behaviour.

Which funding source the buyer picked, when the product offers a choice (see Membership.bankDebit — memberships only). Defaults to CARD. BANK is a request: the server re-checks the operator toggle, the rail, the connected account's capability, and the minimum amount, and silently bills the subscription to a card instead when any of them says no. Unrelated to paymentMethod, which is a saved Stripe PaymentMethod id.

bankDebitMandateID

A bank-debit mandate the buyer has already authorized, as the Stripe SetupIntent id returned by createBankDebitMandateSetup. Required for a rail that can't collect its mandate from a deferred-intent Payment Element (Canadian pre-authorized debit — see BankDebitCheckoutOption.mandateSetupRequired); ignored unless fundingSource is BANK.

With one, the subscription is created already billing the mandated bank account: its first invoice is confirmed server-side rather than by the client, and the returned checkout is only there so the caller can completeCheckout it. The server re-reads the SetupIntent from Stripe and requires it to have succeeded against this buyer's own customer, so an id from anywhere else is refused rather than trusted.

Returns

PurchaseProductResult!

See PurchaseProductResult for the full shape.

Try it

Authentication
Example
Query
mutation PurchaseProduct($product: EntityRef!, $gift: ProductPurchaseGiftInput, $buyer: BuyerInput, $discountCode: String, $giftCardCode: String, $paymentMethod: ID, $savePaymentMethod: Boolean, $taxCalculationId: String, $forRegistration: Boolean, $recurringConsentAccepted: Boolean, $emailMarketingConsent: Boolean, $emailMarketingConsentDisclosureVersion: ID, $attribution: AttributionInput, $idempotencyKey: String, $fundingSource: CheckoutFundingSource, $bankDebitMandate: ID) {
  purchaseProduct(product: $product, gift: $gift, buyer: $buyer, discountCode: $discountCode, giftCardCode: $giftCardCode, paymentMethod: $paymentMethod, savePaymentMethod: $savePaymentMethod, taxCalculationId: $taxCalculationId, forRegistration: $forRegistration, recurringConsentAccepted: $recurringConsentAccepted, emailMarketingConsent: $emailMarketingConsent, emailMarketingConsentDisclosureVersion: $emailMarketingConsentDisclosureVersion, attribution: $attribution, idempotencyKey: $idempotencyKey, fundingSource: $fundingSource, bankDebitMandate: $bankDebitMandate) {
    __typename
  }
}
Variables
{}
POST https://go.sessions.website/api/graphql