> ## Documentation Index
> Fetch the complete documentation index at: https://docs.base44.com/llms.txt
> Use this file to discover all available pages before exploring further.

# payments

***

## Overview

Payments module for accepting one-time payments and subscriptions in your app through its connected payment provider.

This module provides:

* Checkouts for one-time payments and subscriptions, on the provider's hosted page or embedded in your app
* Purchase lookup
* Refunds
* Subscription management, including canceling, pausing, and resuming

### Providers

Each app connects one provider, which is Stripe, PayPal, or Base44 Payments powered by Wix.

| | Stripe | PayPal | Base44 Payments |
| :- | :- | :- | :- |
| Redirect checkout | Yes | No | Yes |
| Embedded checkout | Yes | Yes, required | No |
| Refunds | Yes | Yes | No |
| Subscription cancellation | Yes, now or at period end | Yes, now only | No |
| Subscription pause and resume | Yes | Yes | No |

### Authentication modes

* **Anonymous or user authentication** (`base44.payments`): Preloads and mounts embedded checkouts, completes PayPal checkouts, and lists the current user's own purchases.
* **Service role authentication** (`base44.asServiceRole.payments`): Creates checkouts, lists any purchase, refunds payments, and changes subscriptions. Use the [service role methods](#service-role-methods) in backend functions.

## Methods

These methods use anonymous or user authentication, through `base44.payments`.

### preloadCheckout()

> **preloadCheckout**(`provider`): `Promise`\<`void`>

Starts downloading the provider's JavaScript library, Stripe.js or the PayPal JavaScript SDK, before you mount a checkout, so the checkout appears faster.

Call it when the checkout page or form opens, while the buyer is still filling it in. [`mountCheckout()`](#mountcheckout) needs the library to render the checkout, and downloads it itself if you didn't preload it.

It never rejects, so you don't need to await it or catch errors. If the library can't load, `mountCheckout()` tries again and rejects with the error. Loading the library requires a browser environment and is skipped in the backend.

#### Parameters

**provider** `any` *required*

The provider the app is connected to.

`"stripe"` | `"paypal"`

#### Returns

`Promise<void>`

Promise resolving once the library has loaded or failed to load.

#### Example

```typescript Preload PayPal theme={null}
base44.payments.preloadCheckout('paypal');
```

***

### mountCheckout()

> **mountCheckout**(`element`, `checkout`, `options?`): `Promise`\<`CheckoutMount`>

Renders an embedded checkout inside an element on the page.

Pass the result of a [`createCheckout()`](#createcheckout) call made with `presentation: 'embedded'`. Because only the service role can create checkouts, create it in a backend function and return the result to the page.

The SDK loads the provider's library and renders the checkout for you:

* PayPal: Renders a PayPal button. When the buyer approves the payment, the SDK calls [`completeCheckout()`](#completecheckout) and then `onComplete`.
* Stripe: Mounts Stripe Embedded Checkout. After paying, the buyer returns to the checkout's `success_path`. Stripe allows one embedded checkout per page, so mounting another Stripe checkout destroys the previous one.

To make the checkout appear faster, load the library ahead of time with [`preloadCheckout()`](#preloadcheckout) when the page opens.

Requires a browser environment and can't be used in the backend. Rejects when the checkout isn't embedded, when the payments config can't be fetched or has no valid script URL for the provider, when the provider's library fails to load, or when PayPal isn't available for the checkout's currency.

The checkout's callbacks run in the buyer's browser, where they can be faked. Grant access to what the buyer paid for only after your backend confirms the purchase, for example with [`listPurchases()`](#service-role-methods) in a backend function.

#### Parameters

> [accordion] Properties
>
> **element** `CheckoutElement` *required*
>
> Element to render the checkout in. It should be empty.
>
> **checkout** `MountableCheckout` *required*
>
> The result of [`createCheckout()`](#createcheckout).
>
> > [accordion] Properties
> >
> > **checkout\_id** `string` *required*
> >
> > ID of the checkout.
> >
> > **currency** `string` *required*
> >
> > Three-letter ISO currency code of the checkout.
> >
> > **embedded** `EmbeddedCheckout | null` *required*
> >
> > Data for the embedded checkout. Mounting rejects when it's `null`.
>
> **options** `MountCheckoutOptions`
>
> Callbacks for the checkout's outcome.
>
> > [accordion] Properties
> >
> > **onComplete** `(result: CompleteCheckoutResult) =\> void`
> >
> > Called after the buyer approves a PayPal payment and the SDK completes the checkout. Use it to update the page, not to grant access.
> >
> > Stripe doesn't call this. After a Stripe payment, the buyer returns to the checkout's `success_path` with a `session_id` query parameter. That parameter is the `checkout_id`. Show the result there with [`listPurchases()`](#listpurchases).
> >
> > **onCancel** `() =\> void`
> >
> > Called when the buyer closes the PayPal window without paying. The button stays mounted so the buyer can try again.
> >
> > **onError** `(error: unknown) =\> void`
> >
> > Called when a PayPal checkout fails after it's mounted, including a declined payment with the [`payment_declined`](#paymentserrorcode) code. The button stays mounted so the buyer can try again. If it keeps failing with `payment_declined`, create a new checkout and mount it. Stripe shows its own errors inside the checkout.
> >
> > The error is a `Base44Error` when completing the checkout fails, or PayPal's own error object when the PayPal window fails.

#### Returns

`CheckoutMount`

Promise resolving to the mounted checkout once it renders.

> [accordion] Properties
>
> **destroy** `() =\> void` *required*
>
> Removes the checkout from the page. Call it when the component that holds it unmounts. For Stripe, mounting another checkout destroys this one for you.

#### Examples

```typescript Mount a checkout theme={null}
// Create the checkout in a backend function
const { data: checkout } = await base44.functions.invoke('createCheckout', { planId });

const mount = await base44.payments.mountCheckout(
  document.getElementById('checkout')!,
  checkout,
  {
    onComplete: ({ status }) => navigate(status === 'paid' ? '/thank-you' : '/processing'),
    onError: () => setError('The payment failed. Please try again.'),
  }
);

// Later, when the page unmounts
mount.destroy();
```

```tsx React component theme={null}
function Checkout({ checkout }: { checkout: CreateCheckoutResult }) {
  const ref = useRef<HTMLDivElement>(null);

  useEffect(() => {
    let mount: CheckoutMount | undefined;
    let cancelled = false;
    base44.payments
      .mountCheckout(ref.current!, checkout, { onComplete: () => navigate('/thank-you') })
      .then((m) => (cancelled ? m.destroy() : (mount = m)))
      .catch(() => setError('The checkout could not load.'));
    return () => {
      cancelled = true;
      mount?.destroy();
    };
  }, [checkout]);

  return <div ref={ref} />;
}
```

***

### completeCheckout()

> **completeCheckout**(`checkoutId`): `Promise`\<`CompleteCheckoutResult`>

Completes a PayPal checkout after the buyer approves it.

When you use [`mountCheckout()`](#mountcheckout), it calls this for you. Call it directly only if you render your own PayPal button. Calling it again for the same checkout returns the same result.

Only PayPal checkouts need completing. Other providers reject with [`complete_not_supported`](#paymentserrorcode).

To complete the checkout from a backend function instead, use [`completeCheckout()`](#service-role-methods) with the service role.

#### Parameters

**checkoutId** `string` *required*

The `checkout_id` from [`createCheckout()`](#createcheckout).

#### Returns

`CompleteCheckoutResult`

Promise resolving to the checkout's payment status.

> [accordion] Properties
>
> **checkout\_id** `string` *required*
>
> ID of the checkout.
>
> **status** `"pending" | "paid"` *required*
>
> Whether the payment went through or the provider is still processing it.

#### Example

```typescript Complete a PayPal checkout theme={null}
// In your own PayPal onApprove handler
const { status } = await base44.payments.completeCheckout(checkoutId);
if (status === 'paid') {
  showThankYou();
}
```

***

### listPurchases()

> **listPurchases**(`params?`): `Promise`\<`ListPurchasesResult`>

Lists the current user's purchases. A purchase belongs to the user when it was made with the user's email address.

To confirm a purchase in a backend function before you grant access, or to look up any buyer's purchases, use [`listPurchases()`](#service-role-methods) with the service role.

PayPal can't list purchases, so for PayPal apps, pass `checkout_id` or `subscription_id`. The call looks up that one purchase and rejects with [`invalid_request`](#paymentserrorcode) without either.

Rejects with [`forbidden`](#paymentserrorcode) when the client uses anonymous authentication.

#### Parameters

**params** `ListPurchasesParams`

Optional filters and pagination.

> [accordion] Properties
>
> **checkout\_id** `string`
>
> Only return the purchase from this checkout.
>
> **subscription\_id** `string`
>
> Only return purchases of this subscription.
>
> **limit** `number`
>
> Maximum number of purchases to return, from 1 to 100. Defaults to `20`.
>
> **cursor** `string`
>
> The `next_cursor` from the previous page. Send the same filters and `limit` as that call.

#### Returns

`ListPurchasesResult`

Promise resolving to a page of purchases.

> [accordion] Properties
>
> **items** `Purchase[]` *required*
>
> Purchases on this page.
>
> > [accordion] Properties
> >
> > **checkout\_id** `string` *required*
> >
> > ID of the checkout the purchase came from.
> >
> > **status** `PurchaseStatus` *required*
> >
> > Status of the purchase.
> >
> > * `'pending'`: The buyer hasn't finished paying, or the provider is still processing the payment.
> > * `'paid'`: The payment went through.
> > * `'refunded'`: The payment was refunded.
> > * `'canceled'`: The checkout or subscription was canceled.
> > * `'failed'`: The payment failed.
> > * `'unknown'`: The provider reported a state that doesn't map to the others.
> >
> > **items** `PurchaseItem[]` *required*
> >
> > Line items of the purchase.
> >
> > > [accordion] Properties
> > >
> > > **name** `string` *required*
> > >
> > > Item name.
> > >
> > > **amount** `number | null`
> > >
> > > Unit price in minor units, when the provider reports it.
> > >
> > > **quantity** `number | null`
> > >
> > > Number of units, when the provider reports it.
> >
> > **amount\_total** `number | null`
> >
> > Total amount in minor units.
> >
> > **currency** `string | null`
> >
> > Three-letter ISO currency code, in lowercase.
> >
> > **buyer\_email** `string | null`
> >
> > Buyer's email address.
> >
> > **metadata** `Record\<string, string\> | null`
> >
> > The metadata passed to [`createCheckout()`](#createcheckout). Always `null` unless you call with the service role.
> >
> > **subscription\_id** `string | null`
> >
> > ID of the subscription, for recurring purchases.
> >
> > **shipping\_address** `ShippingAddress | null`
> >
> > Where the order ships, whether your app or the provider collected the address. Always `null` unless you call with the service role. Save it on your order record.
> >
> > > [accordion] Properties
> > >
> > > **name** `string` *required*
> > >
> > > Recipient's full name, 1 to 300 characters.
> > >
> > > **line1** `string` *required*
> > >
> > > Street address, 1 to 300 characters.
> > >
> > > **line2** `string | null`
> > >
> > > Apartment, suite, or building, up to 300 characters.
> > >
> > > **city** `string | null`
> > >
> > > City, up to 120 characters. Some countries address by region and postal code alone.
> > >
> > > **region** `string | null`
> > >
> > > State, province, or region, up to 300 characters.
> > >
> > > **postal\_code** `string | null`
> > >
> > > Postal or ZIP code, up to 60 characters.
> > >
> > > **country** `string` *required*
> > >
> > > Two-letter ISO 3166-1 country code, such as `'US'`.
> >
> > **shipping\_amount** `number | null`
> >
> > Shipping fee in minor units, when the checkout charged one.
> >
> > **payment\_id** `string | null`
> >
> > ID of the payment to pass to [`refund()`](#refund). For a PayPal subscription it's the latest charge, returned only with the service role. Read it when you start a refund and reuse it on retries. It's `null` when the provider doesn't name a payment, such as a PayPal subscription whose latest charge is already refunded.
>
> **next\_cursor** `string | null` *required*
>
> Cursor for the next page, or `null` on the last page.

#### Examples

```typescript Check a purchase by checkout ID theme={null}
const { items } = await base44.payments.listPurchases({ checkout_id: checkoutId });
const paid = items[0]?.status === 'paid';
```

```typescript Stripe success page theme={null}
// On a Stripe success page, the session_id query parameter is the checkout ID
const checkoutId = new URLSearchParams(window.location.search).get('session_id');

// Filtering by checkout returns at most one purchase
const { items: [purchase] } = await base44.payments.listPurchases({ checkout_id: checkoutId });

if (purchase?.status === 'paid') {
  showThankYou();
}
```

```typescript Page through purchases theme={null}
let cursor: string | undefined;
do {
  const page = await base44.payments.listPurchases({ limit: 50, cursor });
  page.items.forEach((purchase) => console.log(purchase.checkout_id, purchase.status));
  cursor = page.next_cursor ?? undefined;
} while (cursor);
```

## Service role methods

These methods use service role authentication, through `base44.asServiceRole.payments`. Call them from backend functions.

Not every provider supports every operation. See [Providers](#providers) for which ones do.

Refunds take a required `idempotency_key` and checkouts take an optional one, so retrying them doesn't charge or refund twice. Repeating a subscription change returns the subscription's current state.

### createCheckout()

> **createCheckout**(`params`): `Promise`\<`CreateCheckoutResult`>

Creates a checkout.

A checkout is either a redirect checkout, where the buyer leaves your app for the provider's hosted checkout page, or an embedded checkout, which renders inside your app. Choose one with `presentation`. It defaults to `'redirect'`.

After you create the checkout, use the result:

* Redirect checkout: Send the buyer to the returned `redirect_url`.
* Embedded checkout: Return the result to the page and pass it to [`mountCheckout()`](#mountcheckout).

A checkout with a `recurring` item creates a subscription.

#### Parameters

**params** `CreateCheckoutParams` *required*

Items, return paths, and options for the checkout.

> [accordion] Properties
>
> **items** `CheckoutItem[]` *required*
>
> Items to sell, 1 to 100 entries.
>
> > [accordion] Properties
> >
> > **name** `string` *required*
> >
> > Item name shown to the buyer, 1 to 255 characters.
> >
> > **amount** `number` *required*
> >
> > Unit price as an integer in the currency's minor units, such as cents. Must be greater than `0`. Providers may also reject a total below their minimum charge.
> >
> > **quantity** `number`
> >
> > Number of units, from 1 to 1,000,000. Defaults to `1`.
> >
> > **shippable** `boolean`
> >
> > Whether the item ships. For a shippable item, the provider collects a shipping address unless you pass one in `shipping.address`.
> >
> > **recurring** `CheckoutItemRecurring`
> >
> > Billing terms. Set this to sell the item as a subscription. PayPal and Base44 Payments allow one recurring item per checkout, and PayPal allows no other items with it.
> >
> > > [accordion] Properties
> > >
> > > **frequency** `PaymentsRecurringFrequency` *required*
> > >
> > > Unit of the billing period.
> > >
> > > It's `'day'`, `'week'`, `'month'`, or `'year'`.
> > >
> > > **interval** `number`
> > >
> > > Number of `frequency` units between charges, from 1 to 3650. Defaults to `1`.
> > >
> > > **auto\_renewal** `boolean`
> > >
> > > Whether the subscription renews automatically at the end of its billing cycles. Stripe doesn't support `false`.
> > >
> > > **billing\_cycles** `number`
> > >
> > > Number of billing cycles before the subscription ends, at least 1. Omit for no limit. Not supported for Stripe.
> > >
> > > **free\_trial** `CheckoutFreeTrial`
> > >
> > > Free trial before the first charge. Stripe applies one trial to the whole checkout, so every recurring item must request the same trial.
> > >
> > > > [accordion] Properties
> > > >
> > > > **frequency** `PaymentsRecurringFrequency` *required*
> > > >
> > > > Unit of the trial length, which is `'day'`, `'week'`, `'month'`, or `'year'`. Stripe supports only `'day'` and `'week'`.
> > > >
> > > > It's `'day'`, `'week'`, `'month'`, or `'year'`.
> > > >
> > > > **interval** `number` *required*
> > > >
> > > > Number of `frequency` units the trial lasts, from 1 to 999.
> > >
> > > **title** `string`
> > >
> > > Subscription title shown to the buyer, up to 150 characters.
> > >
> > > **description** `string`
> > >
> > > Subscription description shown to the buyer, up to 500 characters.
>
> **currency** `string`
>
> Three-letter ISO currency code, such as `'usd'`. Defaults to the payment account's currency. Required for PayPal.
>
> **success\_path** `string` *required*
>
> App-relative path the buyer returns to after paying, such as `'/thank-you'`. Must start with `/`.
>
> **cancel\_path** `string`
>
> App-relative path the buyer returns to after canceling. Must start with `/`. Not supported for Stripe embedded checkout.
>
> **buyer** `CheckoutBuyer`
>
> Details about the buyer, used to prefill the checkout.
>
> > [accordion] Properties
> >
> > **email** `string`
> >
> > Buyer's email address.
> >
> > **first\_name** `string`
> >
> > Buyer's first name.
> >
> > **last\_name** `string`
> >
> > Buyer's last name.
> >
> > **phone** `string`
> >
> > Buyer's phone number.
>
> **metadata** `Record\<string, string\>`
>
> Your own key-value pairs, up to 50 keys. Returned in each purchase's `metadata` to service role callers only. Not supported for PayPal.
>
> **idempotency\_key** `string`
>
> Key that makes retries return the same checkout instead of creating another, 1 to 200 characters. Not supported for Base44 Payments or PayPal subscriptions.
>
> **presentation** `CheckoutPresentation`
>
> How the buyer pays. Defaults to `'redirect'`. PayPal apps must pass `'embedded'`, and Base44 Payments supports only `'redirect'`.
>
> * `'redirect'`: The buyer leaves the app for the provider's hosted checkout page at `redirect_url`.
> * `'embedded'`: The checkout renders inside the app with [`mountCheckout()`](#mountcheckout).
>
> **shipping** `CheckoutShipping`
>
> Shipping address and fee. Allowed only when at least one item is `shippable`. Supported for PayPal one-time checkouts only. Other providers and subscriptions reject it with [`invalid_request`](#paymentserrorcode).
>
> Compute the fee in a backend function, never in the browser. When the fee depends on the address, such as by distance or zone, collect the address in your app and pass it in `address`. PayPal shows the address to the buyer locked, so the order ships to the address you computed the fee for. When the fee doesn't depend on the address, pass only `amount` and PayPal asks the buyer for the address.
>
> > [accordion] Properties
> >
> > **address** `ShippingAddress`
> >
> > Address your app collected. Omit it to let the provider ask the buyer.
> >
> > > [accordion] Properties
> > >
> > > **name** `string` *required*
> > >
> > > Recipient's full name, 1 to 300 characters.
> > >
> > > **line1** `string` *required*
> > >
> > > Street address, 1 to 300 characters.
> > >
> > > **line2** `string | null`
> > >
> > > Apartment, suite, or building, up to 300 characters.
> > >
> > > **city** `string | null`
> > >
> > > City, up to 120 characters. Some countries address by region and postal code alone.
> > >
> > > **region** `string | null`
> > >
> > > State, province, or region, up to 300 characters.
> > >
> > > **postal\_code** `string | null`
> > >
> > > Postal or ZIP code, up to 60 characters.
> > >
> > > **country** `string` *required*
> > >
> > > Two-letter ISO 3166-1 country code, such as `'US'`.
> >
> > **amount** `number`
> >
> > Shipping fee as an integer in minor units, shown to the buyer as its own shipping line. Omit it for free shipping.

#### Returns

`CreateCheckoutResult`

Promise resolving to the checkout. Exactly one of `redirect_url` and `embedded` is set, depending on the checkout's `presentation`.

> [accordion] Properties
>
> **checkout\_id** `string` *required*
>
> ID of the checkout. For Stripe it's the Checkout Session ID, and for PayPal it's the PayPal order or subscription ID.
>
> **currency** `string` *required*
>
> Three-letter ISO currency code of the checkout, in lowercase.
>
> **amount\_total** `number` *required*
>
> Amount the buyer pays now, in minor units. It's `0` for a subscription that starts with a free trial.
>
> **redirect\_url** `string | null` *required*
>
> Absolute URL of the provider's hosted checkout page, for redirect checkouts.
>
> **embedded** `EmbeddedCheckout | null` *required*
>
> Data for [`mountCheckout()`](#mountcheckout), for embedded checkouts.

#### Examples

```typescript Redirect checkout theme={null}
import { createClientFromRequest } from 'npm:@base44/sdk';

Deno.serve(async (req) => {
  const base44 = createClientFromRequest(req);
  const checkout = await base44.asServiceRole.payments.createCheckout({
    items: [{ name: 'T-shirt', amount: 2500, quantity: 2, shippable: true }],
    currency: 'usd',
    success_path: '/thank-you',
    cancel_path: '/cart',
  });

  return Response.json({ url: checkout.redirect_url });
});
```

```typescript Embedded subscription checkout theme={null}
import { createClientFromRequest } from 'npm:@base44/sdk';

Deno.serve(async (req) => {
  const base44 = createClientFromRequest(req);
  const user = await base44.auth.me();
  const checkout = await base44.asServiceRole.payments.createCheckout({
    items: [{
      name: 'Pro plan',
      amount: 1500,
      recurring: { frequency: 'month', free_trial: { frequency: 'day', interval: 14 } },
    }],
    currency: 'usd',
    success_path: '/welcome',
    buyer: { email: user.email },
    presentation: 'embedded',
  });

  // Pass this to base44.payments.mountCheckout() on the page
  return Response.json(checkout);
});
```

***

### completeCheckout()

> **completeCheckout**(`checkoutId`): `Promise`\<`CompleteCheckoutResult`>

Completes a PayPal checkout from a backend function, after the buyer approves it.

Use it when you render your own PayPal button and its `onApprove` handler sends the checkout ID to your backend. Calling it again for the same checkout returns the same result.

Only PayPal checkouts need completing. Other providers reject with [`complete_not_supported`](#paymentserrorcode).

#### Parameters

**checkoutId** `string` *required*

The `checkout_id` from [`createCheckout()`](#createcheckout).

#### Returns

`CompleteCheckoutResult`

Promise resolving to the checkout's payment status.

> [accordion] Properties
>
> **checkout\_id** `string` *required*
>
> ID of the checkout.
>
> **status** `"pending" | "paid"` *required*
>
> Whether the payment went through or the provider is still processing it.

#### Example

```typescript Complete a PayPal checkout theme={null}
const { status } = await base44.asServiceRole.payments.completeCheckout(checkoutId);
```

***

### listPurchases()

> **listPurchases**(`params?`): `Promise`\<`ListPurchasesResult`>

Lists the app's purchases.

Unlike [`base44.payments.listPurchases()`](#listpurchases), it returns any buyer's purchases and includes their `metadata`.

PayPal can't list purchases, so for PayPal apps, pass `checkout_id` or `subscription_id`. The call looks up that one purchase and rejects with [`invalid_request`](#paymentserrorcode) without either.

#### Parameters

**params** `ServiceRoleListPurchasesParams`

Optional filters and pagination.

> [accordion] Properties
>
> **checkout\_id** `string`
>
> Only return the purchase from this checkout.
>
> **subscription\_id** `string`
>
> Only return purchases of this subscription.
>
> **limit** `number`
>
> Maximum number of purchases to return, from 1 to 100. Defaults to `20`.
>
> **cursor** `string`
>
> The `next_cursor` from the previous page. Send the same filters and `limit` as that call.
>
> **buyer\_email** `string`
>
> Only return purchases made with this email address.

#### Returns

`ListPurchasesResult`

Promise resolving to a page of purchases.

> [accordion] Properties
>
> **items** `Purchase[]` *required*
>
> Purchases on this page.
>
> > [accordion] Properties
> >
> > **checkout\_id** `string` *required*
> >
> > ID of the checkout the purchase came from.
> >
> > **status** `PurchaseStatus` *required*
> >
> > Status of the purchase.
> >
> > * `'pending'`: The buyer hasn't finished paying, or the provider is still processing the payment.
> > * `'paid'`: The payment went through.
> > * `'refunded'`: The payment was refunded.
> > * `'canceled'`: The checkout or subscription was canceled.
> > * `'failed'`: The payment failed.
> > * `'unknown'`: The provider reported a state that doesn't map to the others.
> >
> > **items** `PurchaseItem[]` *required*
> >
> > Line items of the purchase.
> >
> > > [accordion] Properties
> > >
> > > **name** `string` *required*
> > >
> > > Item name.
> > >
> > > **amount** `number | null`
> > >
> > > Unit price in minor units, when the provider reports it.
> > >
> > > **quantity** `number | null`
> > >
> > > Number of units, when the provider reports it.
> >
> > **amount\_total** `number | null`
> >
> > Total amount in minor units.
> >
> > **currency** `string | null`
> >
> > Three-letter ISO currency code, in lowercase.
> >
> > **buyer\_email** `string | null`
> >
> > Buyer's email address.
> >
> > **metadata** `Record\<string, string\> | null`
> >
> > The metadata passed to [`createCheckout()`](#createcheckout). Always `null` unless you call with the service role.
> >
> > **subscription\_id** `string | null`
> >
> > ID of the subscription, for recurring purchases.
> >
> > **shipping\_address** `ShippingAddress | null`
> >
> > Where the order ships, whether your app or the provider collected the address. Always `null` unless you call with the service role. Save it on your order record.
> >
> > > [accordion] Properties
> > >
> > > **name** `string` *required*
> > >
> > > Recipient's full name, 1 to 300 characters.
> > >
> > > **line1** `string` *required*
> > >
> > > Street address, 1 to 300 characters.
> > >
> > > **line2** `string | null`
> > >
> > > Apartment, suite, or building, up to 300 characters.
> > >
> > > **city** `string | null`
> > >
> > > City, up to 120 characters. Some countries address by region and postal code alone.
> > >
> > > **region** `string | null`
> > >
> > > State, province, or region, up to 300 characters.
> > >
> > > **postal\_code** `string | null`
> > >
> > > Postal or ZIP code, up to 60 characters.
> > >
> > > **country** `string` *required*
> > >
> > > Two-letter ISO 3166-1 country code, such as `'US'`.
> >
> > **shipping\_amount** `number | null`
> >
> > Shipping fee in minor units, when the checkout charged one.
> >
> > **payment\_id** `string | null`
> >
> > ID of the payment to pass to [`refund()`](#refund). For a PayPal subscription it's the latest charge, returned only with the service role. Read it when you start a refund and reuse it on retries. It's `null` when the provider doesn't name a payment, such as a PayPal subscription whose latest charge is already refunded.
>
> **next\_cursor** `string | null` *required*
>
> Cursor for the next page, or `null` on the last page.

#### Examples

```typescript Confirm a purchase before granting access theme={null}
import { createClientFromRequest } from 'npm:@base44/sdk';

Deno.serve(async (req) => {
  const base44 = createClientFromRequest(req);
  const { checkoutId } = await req.json();

  const { items: [purchase] } = await base44.asServiceRole.payments.listPurchases({
    checkout_id: checkoutId,
  });

  if (purchase?.status !== 'paid') {
    return Response.json({ error: 'Payment not confirmed' }, { status: 402 });
  }

  // Grant access to what the buyer paid for here
  return Response.json({ ok: true });
});
```

```typescript Find a buyer's purchases theme={null}
// Not available for PayPal apps, which need checkout_id or subscription_id
const { items } = await base44.asServiceRole.payments.listPurchases({
  buyer_email: 'buyer@example.com',
});
```

***

### refund()

> **refund**(`params`): `Promise`\<`RefundResult`>

Refunds all or part of a payment.

Retrying with the same `idempotency_key` and `amount` returns the original refund instead of refunding twice. When the call rejects with [`refund_failed`](#paymentserrorcode), the outcome is unknown, so retry it with the same values.

Not supported for Base44 Payments.

#### Parameters

**params** `RefundParams` *required*

The payment, amount, and idempotency key.

> [accordion] Properties
>
> **payment\_id** `string` *required*
>
> The `payment_id` of the purchase to refund.
>
> **amount** `number` *required*
>
> Amount to refund as an integer in minor units. Must be greater than `0`.
>
> **idempotency\_key** `string` *required*
>
> Key that makes retries return the original refund instead of refunding twice, 1 to 100 characters.

#### Returns

`RefundResult`

Promise resolving to the refund.

> [accordion] Properties
>
> **refund\_id** `string` *required*
>
> Provider's ID of the refund.
>
> **payment\_id** `string` *required*
>
> ID of the refunded payment.
>
> **amount** `number` *required*
>
> Refunded amount in minor units.
>
> **currency** `string` *required*
>
> Three-letter ISO currency code, in lowercase.
>
> **status** `"pending" | "succeeded"` *required*
>
> Whether the refund went through or the provider is still processing it.

#### Example

```typescript Full refund theme={null}
const { items } = await base44.asServiceRole.payments.listPurchases({ checkout_id: checkoutId });
const purchase = items[0];

const refund = await base44.asServiceRole.payments.refund({
  payment_id: purchase.payment_id!,
  amount: purchase.amount_total!,
  idempotency_key: `refund-${checkoutId}`,
});
```

***

### cancelSubscription()

> **cancelSubscription**(`subscriptionId`, `options?`): `Promise`\<`SubscriptionResult`>

Cancels a subscription, now or at the end of the current billing period.

Not supported for Base44 Payments. PayPal supports canceling now only.

#### Parameters

> [accordion] Properties
>
> **subscriptionId** `string` *required*
>
> The `subscription_id` of the purchase.
>
> **options** `CancelSubscriptionOptions`
>
> When to cancel.
>
> > [accordion] Properties
> >
> > **at\_period\_end** `boolean`
> >
> > Cancel at the end of the current billing period instead of now. Defaults to `false`. Not supported for PayPal.

#### Returns

`SubscriptionResult`

Promise resolving to the subscription's state.

> [accordion] Properties
>
> **subscription\_id** `string` *required*
>
> ID of the subscription.
>
> **status** `"canceled" | "active" | "past_due" | "paused"` *required*
>
> Current status of the subscription.
>
> **cancel\_at\_period\_end** `boolean` *required*
>
> Whether the subscription is set to cancel at the end of the current billing period.

#### Example

```typescript Cancel at period end theme={null}
await base44.asServiceRole.payments.cancelSubscription(subscriptionId, {
  at_period_end: true,
});
```

***

### pauseSubscription()

> **pauseSubscription**(`subscriptionId`): `Promise`\<`SubscriptionResult`>

Pauses a subscription's billing.

Not supported for Base44 Payments.

#### Parameters

**subscriptionId** `string` *required*

The `subscription_id` of the purchase.

#### Returns

`SubscriptionResult`

Promise resolving to the subscription's state.

> [accordion] Properties
>
> **subscription\_id** `string` *required*
>
> ID of the subscription.
>
> **status** `"canceled" | "active" | "past_due" | "paused"` *required*
>
> Current status of the subscription.
>
> **cancel\_at\_period\_end** `boolean` *required*
>
> Whether the subscription is set to cancel at the end of the current billing period.

#### Example

```typescript Pause a subscription theme={null}
await base44.asServiceRole.payments.pauseSubscription(subscriptionId);
```

***

### resumeSubscription()

> **resumeSubscription**(`subscriptionId`): `Promise`\<`SubscriptionResult`>

Resumes a paused subscription's billing.

Not supported for Base44 Payments.

#### Parameters

**subscriptionId** `string` *required*

The `subscription_id` of the purchase.

#### Returns

`SubscriptionResult`

Promise resolving to the subscription's state.

> [accordion] Properties
>
> **subscription\_id** `string` *required*
>
> ID of the subscription.
>
> **status** `"canceled" | "active" | "past_due" | "paused"` *required*
>
> Current status of the subscription.
>
> **cancel\_at\_period\_end** `boolean` *required*
>
> Whether the subscription is set to cancel at the end of the current billing period.

#### Example

```typescript Resume a subscription theme={null}
await base44.asServiceRole.payments.resumeSubscription(subscriptionId);
```

## Errors

### PaymentsErrorCode

> **PaymentsErrorCode** = `"forbidden"` | `"unauthenticated"` | `"not_found"` | `"conflict"` | `"rate_limited"` | `"payments_not_connected"` | `"invalid_request"` | `"invalid_items"` | `"amount_below_minimum"` | `"currency_mismatch"` | `"currency_required"` | `"invalid_metadata"` | `"idempotency_not_supported"` | `"idempotency_key_reused"` | `"multiple_subscription_items"` | `"recurring_option_not_supported"` | `"invalid_cursor"` | `"presentation_not_supported"` | `"embedded_checkout_unavailable"` | `"checkout_not_found"` | `"checkout_not_approved"` | `"checkout_closed"` | `"payment_declined"` | `"complete_not_supported"` | `"provider_unavailable"` | `"payment_not_found"` | `"refund_rejected"` | `"refund_in_progress"` | `"refund_amount_mismatch"` | `"refund_temporarily_unavailable"` | `"refund_failed"` | `"refund_not_supported"` | `"subscription_not_found"` | `"subscription_not_active"` | `"subscription_action_not_supported"`

Error codes that payments methods set in the `code` of a [`Base44Error`](https://docs.base44.com/developers/references/sdk/getting-started/work-with-sdk#error-handling) when they reject. Invalid arguments and [`mountCheckout()`](#mountcheckout) failures reject with a plain `Error` instead.

Notable codes include:

* `payment_declined`: The buyer's payment method was declined when completing the checkout. The buyer can try again with another method.
* `refund_failed`: The refund's outcome is unknown. Retry with the same `idempotency_key` and `amount`, which returns the original refund if one was made.
* `refund_in_progress`: A refund with the same `idempotency_key` is still running.
* `refund_amount_mismatch`: The `idempotency_key` was already used for a different amount.
* `rate_limited`: Too many requests. Wait for the number of seconds in the `Retry-After` response header, available at `error.originalError.response.headers['retry-after']`.
* `refund_not_supported`, `subscription_action_not_supported`, `complete_not_supported`, `presentation_not_supported`: The app's payment provider doesn't support the operation.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.