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

# Payments

> Sessions, statuses and payment flow

## Payment Flow

```mermaid theme={null}
sequenceDiagram
    participant Game as Game
    participant Server as Game Server
    participant API as ToffeePay
    participant Page as Payment Page

    Game->>Server: Request payment for item
    Server->>API: Create Session request
    API->>Server: Returns `session` object with `id` & `url`
    Server->>Game: Send `session.url`
    Game->>Page: Open `session.url` in browser
    Page->>Page: User completes payment

    par Webhook Flow
        API->>Server: Send webhook (signed)
        Server->>Server: Verify webhook signature
        Server->>Server: Process payment & grant items
    and Return URL Flow
        Page->>Game: Redirect to return_url (deep link)
        Game->>Server: Use existing session_id, request status
        Server->>API: Get Session
        API->>Server: Return payment status
        Server->>Game: Confirm payment & show items
    end
```

***

## Available Payment Methods

ToffeePay supports the following payment methods:

* **card**: Traditional credit/debit card payments
* **tokenized\_card**: Saved card tokens for returning customers
* **apple\_pay**: Apple Pay for iOS and Safari users
* **google\_pay**: Google Pay for Android and Chrome users
* **paypal**: PayPal wallet payments
* **paylater**: PayPal's buy now, pay later options
* **venmo**: Venmo wallet payments

The available payment methods are automatically displayed to users on the payment page based on their device, browser, and configured options for your game.

***

## Create a Payment Session

Send a server-side request to [create a payment session](/api-reference/sessions/create).

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    const { session } = await toffee.sessions.create({
      gameId: "space_shooter",
      userId: "player_42",
      item: {
        title: "50 Gold Coins",
        price: 499,
        currency: "USD",
        image: "data:image/png;base64,...",
      },
      returnUrl: "mygame://payment-complete",
      walletEnabled: true,
    });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    resp = await toffee.sessions.create(CreateSessionRequest(
        game_id="space_shooter",
        user_id="player_42",
        item=Item(
            title="50 Gold Coins",
            price=499,
            currency="USD",
            image="data:image/png;base64,...",
        ),
        return_url="mygame://payment-complete",
        wallet_enabled=True,
    ))
    session = resp.session
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    resp, err := toffee.Sessions.Create(ctx, &sdk.CreateSessionRequest{
      GameId: "space_shooter",
      UserId: "player_42",
      Item: &sdk.Item{
        Title:    "50 Gold Coins",
        Price:    499,
        Currency: "USD",
        Image:    "data:image/png;base64,...",
      },
      ReturnUrl:     "mygame://payment-complete",
      WalletEnabled: true,
    })
    session := resp.GetSession()
    ```
  </Tab>

  <Tab title="API">
    ```http theme={null}
    POST /v1/sessions
    Authorization: Bearer <your_access_token>
    Content-Type: application/json

    {
      "game_id": "space_shooter",
      "user_id": "player_42",
      "item": {
        "title": "50 Gold Coins",
        "price": 499,
        "currency": "USD",
        "image": "data:image/png;base64,..."
      },
      "return_url": "mygame://payment-complete",
      "wallet_enabled": true
    }
    ```
  </Tab>
</Tabs>

**Parameters:**

* `game_id`: Your unique game identifier
* `user_id`: Unique identifier for the player
* `item`: The item being purchased
  * `title`: Display name of the item
  * `price`: Price in cents (e.g., 499 = \\\$4.99)
  * `currency`: Three-letter currency code (USD, EUR, etc.)
  * `image`: Base64-encoded image or URL
* `return_url`: Deep link to return to after payment
* `wallet_enabled`: Boolean flag that specifies whether [Toffee Wallet](/wallet) should be enabled or disabled for the session

**Response:** A Session object with the payment URL.

```json theme={null}
{
  "id": "sess_abc123",
  "url": "https://pay.toffeepay.com/sess_abc123",
  "status": "pending",
  "user_id": "player_42",
  "game_id": "space_shooter",
  "item": {
    "title": "50 Gold Coins",
    "price": 499,
    "currency": "USD"
  },
  "created_at": "2023-06-01T12:00:00Z"
}
```

***

## Payment Statuses

### Session Statuses

Payment sessions can have the following statuses:

* `pending`: Payment session created but not yet paid
* `paid`: Payment successfully completed
* `failed`: Payment attempt failed
* `cancelled`: Payment session was cancelled
* `expired`: Payment session has expired

### Payment Statuses

Individual payment objects (created when processing a session) have these statuses:

* `processing`: Payment is being processed by the payment provider
* `authorized`: Payment has been authorized but not yet captured
* `succeeded`: Payment completed successfully
* `cancelled`: Authorized payment was cancelled before capture
* `failed`: Payment processing failed

**Status Flow:**

1. Session starts as `pending`
2. When payment begins, a Payment object is created with status `processing`
3. Payment can transition to:

   * `authorized`: Payment authorized but not captured
   * `succeeded`: Payment completed successfully (either automatically or via [Complete Payment](/api-reference/payments/complete))
   * `cancelled`: Authorized payment was cancelled via [Cancel Payment](/api-reference/payments/cancel)
   * `failed`: Payment processing failed

   **Note:** Whether payments automatically proceed from `authorized` to `succeeded` or require manual completion via [Complete Payment](/api-reference/payments/complete) is configured during game setup.
4. Session status updates based on final payment status:
   * `paid` (if payment succeeded)
   * `cancelled` (if session was cancelled manually via [Cancel Session](/api-reference/sessions/cancel) or after 3 cancelled payment attempts)
   * `failed` (after 3 failed payment attempts)

### Timestamp Fields

Both sessions and payments include relevant timestamp fields:

**Session timestamps:**

* `created_at`: When the session was created
* `paid_at`: When the session was successfully paid (if applicable)
* `failed_at`: When the session failed (if applicable)
* `expired_at`: When the session expired (if applicable)
* `cancelled_at`: When the session was cancelled (if applicable)

**Payment timestamps:**

* `created_at`: When payment processing began
* `authorized_at`: When payment was authorized (if applicable)
* `succeeded_at`: When payment completed successfully (if applicable)
* `cancelled_at`: When payment was cancelled (if applicable)
* `failed_at`: When payment failed (if applicable)

***

## Webhook Events

ToffeePay sends webhooks for the following payment-related events:

### Session Events

* [`session.created`](/api-reference/webhooks/session.created): When a payment session is created
* [`session.paid`](/api-reference/webhooks/session.paid): When a session is successfully paid
* [`session.failed`](/api-reference/webhooks/session.failed): When payment attempt related to this session fails
* [`session.expired`](/api-reference/webhooks/session.expired): When a session expires without payment
* [`session.cancelled`](/api-reference/webhooks/session.cancelled): When session is cancelled by user

### Payment Events

* [`payment.created`](/api-reference/webhooks/payment.created): When a payment is created and processing begins
* [`payment.authorized`](/api-reference/webhooks/payment.authorized): When payment is authorized but not yet captured
* [`payment.succeeded`](/api-reference/webhooks/payment.succeeded): When payment completes successfully
* [`payment.cancelled`](/api-reference/webhooks/payment.cancelled): When an authorized payment is cancelled
* [`payment.failed`](/api-reference/webhooks/payment.failed): When payment attempt fails

See the [Webhooks](/webhooks) page for implementation details and signature verification.

***

## Open the Payment Page

Open the returned payment `url` in the browser or webview.

The user will:

* View the item details and total price
* Pay using Apple Pay, Google Pay, or other supported methods
* Be redirected back to your game via the `return_url`

### Internationalization

You can customize the payment page language by adding a `locale` query parameter to the payment URL:

```
https://pay.toffeepay.com/sess_abc123?locale=es
```

The payment page will display all text (buttons, labels, error messages) in the specified language.

***

## Handle Payment Notifications

ToffeePay notifies you of payment events through two methods:

### Webhooks

When a payment is completed, ToffeePay sends a signed webhook to your backend. See the [Webhooks](/webhooks) page for detailed implementation.

**Sample Payload:**

```json theme={null}
{
  "type": "payment.succeeded",
  "timestamp": "2023-06-01T12:05:00Z",
  "data": {
    // Payment object
  }
}
```

### Return URL

After payment, the user is redirected to your specified `return_url`. This is typically a custom deep link that your game handles.

**Example:**

```json theme={null}
"return_url": "mygame://payment-complete"
```

### Frontend Events

The payment page emits events to the parent window via `postMessage`. This is useful for client-side integrations (e.g., if using an iframe or popup).

**Event Types:**

**Payment Events** (payload includes payment `id`):

* `toffeepay-payment-unreached`
* `toffeepay-payment-success`
* `toffeepay-payment-failed`

**Session Events** (payload includes session `id`):

* `toffeepay-session-paid`
* `toffeepay-session-failed`
* `toffeepay-session-expired`
* `toffeepay-session-cancelled`
* `toffeepay-session-authorized`

***

## Cancel a Payment Session

You can [cancel a payment session](/api-reference/sessions/cancel) that is still in `pending` status:

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    await toffee.sessions.cancel({ id: "sess_abc123" });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    await toffee.sessions.cancel(CancelSessionRequest(id="sess_abc123"))
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    err := toffee.Sessions.Cancel(ctx, &sdk.CancelSessionRequest{Id: "sess_abc123"})
    ```
  </Tab>

  <Tab title="API">
    ```http theme={null}
    POST /v1/sessions/sess_abc123/cancel
    Authorization: Bearer <your_access_token>
    ```
  </Tab>
</Tabs>

When a session is cancelled:

* The session status changes to `cancelled`
* The `cancelled_at` timestamp is set
* A `session.cancelled` webhook event is sent
* The session cannot be paid and the payment URL becomes invalid

***

## Check Session Status

To confirm session status (especially when handling return URLs), you can use either endpoint:

### [Get Session](/api-reference/sessions/get) (Full Details)

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    const { session } = await toffee.sessions.get({ id: "sess_abc123" });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    resp = await toffee.sessions.get(GetSessionRequest(id="sess_abc123"))
    session = resp.session
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    resp, err := toffee.Sessions.Get(ctx, &sdk.GetSessionRequest{Id: "sess_abc123"})
    session := resp.GetSession()
    ```
  </Tab>

  <Tab title="API">
    ```http theme={null}
    GET /v1/sessions/sess_abc123
    Authorization: Bearer <your_access_token>
    ```
  </Tab>
</Tabs>

**Response:**

```json theme={null}
{
  "id": "sess_abc123",
  "url": "https://pay.toffeepay.com/sess_abc123",
  "status": "paid",
  "user_id": "player_42",
  "game_id": "space_shooter",
  "item": {
    "title": "50 Gold Coins",
    "price": 499,
    "currency": "USD"
  },
  "tax": {
    "amount": 50
  },
  "created_at": "2023-06-01T12:00:00Z",
  "paid_at": "2023-06-01T12:05:00Z"
}
```

### [Get Session Status](/api-reference/sessions/status) (Status Only)

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    const { status } = await toffee.sessions.status({ id: "sess_abc123" });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    resp = await toffee.sessions.status(GetSessionStatusRequest(id="sess_abc123"))
    status = resp.status
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    resp, err := toffee.Sessions.Status(ctx, &sdk.GetSessionStatusRequest{Id: "sess_abc123"})
    status := resp.GetStatus()
    ```
  </Tab>

  <Tab title="API">
    ```http theme={null}
    GET /v1/sessions/sess_abc123/status
    Authorization: Bearer <your_access_token>
    ```
  </Tab>
</Tabs>

**Response:**

```json theme={null}
{
  "id": "sess_abc123",
  "status": "paid"
}
```

**Status handling:**

* If `status` is `paid`, grant the items and show success
* If `status` is `pending`, show a waiting screen
* If `status` is `failed`, `cancelled` or `expired`, inform the user and offer to retry

***

## Complete Payment

When your game wants to control when an authorized payment is captured, use [Complete Payment](/api-reference/payments/complete). This is useful for scenarios where you want to authorize payment but only capture it after fulfilling the order (e.g., confirming item availability).

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    await toffee.payments.complete({ id: "pay_xyz789" });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    await toffee.payments.complete(CompletePaymentRequest(id="pay_xyz789"))
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    err := toffee.Payments.Complete(ctx, &sdk.CompletePaymentRequest{Id: "pay_xyz789"})
    ```
  </Tab>

  <Tab title="API">
    ```http theme={null}
    POST /v1/payments/pay_xyz789/complete
    Authorization: Bearer <your_access_token>
    ```
  </Tab>
</Tabs>

***

## Cancel Payment

[Cancel an authorized payment](/api-reference/payments/cancel) before it's captured. This releases the hold on the customer's payment method.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    await toffee.payments.cancel({ id: "pay_xyz789" });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    await toffee.payments.cancel(CancelPaymentRequest(id="pay_xyz789"))
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    err := toffee.Payments.Cancel(ctx, &sdk.CancelPaymentRequest{Id: "pay_xyz789"})
    ```
  </Tab>

  <Tab title="API">
    ```http theme={null}
    POST /v1/payments/pay_xyz789/cancel
    Authorization: Bearer <your_access_token>
    ```
  </Tab>
</Tabs>

***

## Get Payment Details

[Retrieve details](/api-reference/payments/get) of a payment:

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    const { payment } = await toffee.payments.get({ id: "pay_xyz789" });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    resp = await toffee.payments.get(GetPaymentRequest(id="pay_xyz789"))
    payment = resp.payment
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    resp, err := toffee.Payments.Get(ctx, &sdk.GetPaymentRequest{Id: "pay_xyz789"})
    payment := resp.GetPayment()
    ```
  </Tab>

  <Tab title="API">
    ```http theme={null}
    GET /v1/payments/pay_xyz789?with_extra_data=true
    Authorization: Bearer <your_access_token>
    ```
  </Tab>
</Tabs>

**Response:**

```json theme={null}
{
  "id": "pay_xyz789",
  "session_id": "sess_abc123",
  "status": "succeeded",
  "amount": 499,
  "currency": "USD",
  "method": "apple_pay",
  "details": "50 Gold Coins",
  "created_at": "2023-06-01T12:05:00Z",
  "succeeded_at": "2023-06-01T12:05:30Z",
  "extra_data": {
    "real_amount": 499
  }
}
```

***

## List Sessions

[Retrieve historical sessions](/api-reference/sessions/list) for audit and analytics:

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    const { sessions } = await toffee.sessions.list({
      gameId: "space_shooter",
      userId: "player_42",
    });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    resp = await toffee.sessions.list(ListSessionsRequest(
        game_id="space_shooter",
        user_id="player_42",
    ))
    sessions = resp.sessions
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    resp, err := toffee.Sessions.List(ctx, &sdk.ListSessionsRequest{
      GameId: "space_shooter",
      UserId: "player_42",
    })
    sessions := resp.GetSessions()
    ```
  </Tab>

  <Tab title="API">
    ```http theme={null}
    GET /v1/sessions?game_id=space_shooter&user_id=player_42&status=paid&limit=50&offset=0
    Authorization: Bearer <your_access_token>
    ```
  </Tab>
</Tabs>

**Response:**

```json theme={null}
[
  {
    "id": "sess_abc123",
    "status": "paid",
    "user_id": "player_42",
    "game_id": "space_shooter",
    "item": {
      "title": "50 Gold Coins",
      "price": 499,
      "currency": "USD"
    },
    "tax": {
      "amount": 50
    },
    "created_at": "2023-06-01T12:00:00Z",
    "paid_at": "2023-06-01T12:05:00Z"
  }
]
```

### Filtering by Date

You can use the `from` and `to` parameters (in RFC 3339 format) to retrieve records created within a specific timeframe. This works identically across both Sessions and Payments.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    const { sessions } = await toffee.sessions.list({
      gameId: "space_shooter",
      from: "2024-05-01T00:00:00Z",
      to: "2024-05-31T23:59:59Z",
    });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    resp = await toffee.sessions.list(ListSessionsRequest(
        game_id="space_shooter",
        from="2024-05-01T00:00:00Z",
        to="2024-05-31T23:59:59Z",
    ))
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    resp, err := toffee.Sessions.List(ctx, &sdk.ListSessionsRequest{
      GameId: "space_shooter",
      From:   timestamppb.New(time.Date(2024, 5, 1, 0, 0, 0, 0, time.UTC)),
      To:     timestamppb.New(time.Date(2024, 5, 31, 23, 59, 59, 0, time.UTC)),
    })
    ```
  </Tab>

  <Tab title="REST API">
    ```http theme={null}
    GET /v1/sessions?game_id=space_shooter&from=2024-05-01T00:00:00Z&to=2024-05-31T23:59:59Z
    Authorization: Bearer <your_access_token>
    ```
  </Tab>

  <Tab title="Connect RPC">
    ```bash theme={null}
    curl -X POST https://api.toffeepay.com/pay.v1.PaymentService/ListSessions       -H "Content-Type: application/json"       -H "Authorization: Bearer <your_access_token>"       -d '{
        "game_id": "space_shooter",
        "from": "2024-05-01T00:00:00Z",
        "to": "2024-05-31T23:59:59Z"
      }'
    ```
  </Tab>
</Tabs>

***

## List Payments

[Retrieve historical payments](/api-reference/payments/list) (successful transactions only):

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    const { payments } = await toffee.payments.list({
      gameId: "space_shooter",
      userId: "player_42",
    });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    resp = await toffee.payments.list(ListPaymentsRequest(
        game_id="space_shooter",
        user_id="player_42",
    ))
    payments = resp.payments
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    resp, err := toffee.Payments.List(ctx, &sdk.ListPaymentsRequest{
      GameId: "space_shooter",
      UserId: "player_42",
    })
    payments := resp.GetPayments()
    ```
  </Tab>

  <Tab title="API">
    ```http theme={null}
    GET /v1/payments?game_id=space_shooter&user_id=player_42&limit=50&offset=0&with_extra_data=true
    Authorization: Bearer <your_access_token>
    ```
  </Tab>
</Tabs>

**Response:**

```json theme={null}
[
  {
    "id": "pay_xyz789",
    "session_id": "sess_abc123",
    "status": "succeeded",
    "amount": 499,
    "currency": "USD",
    "method": "apple_pay",
    "details": "50 Gold Coins",
    "created_at": "2023-06-01T12:05:00Z",
    "succeeded_at": "2023-06-01T12:05:30Z",
    "extra_data": {
      "real_amount": 499
    }
  }
]
```

***

## Tax Handling

ToffeePay automatically calculates and applies taxes based on the customer's location. Tax information is included in session responses when the session is completed.

### Tax Structure

The tax object in session responses contains:

* `amount`: Tax amount in cents (same currency as the item price)

### Example

For a \\$4.99 item with \\$0.50 tax:

```json theme={null}
{
  "item": {
    "price": 499,
    "currency": "USD"
  },
  "tax": {
    "amount": 50
  }
}
```

The customer will be charged the total amount (item price + tax = \\\$5.49 in this example).

**Note:** Tax calculations are handled automatically during payment processing. The tax amount is determined at the time of payment based on the customer's billing address.

***

## Image Requirements

Item images must meet these requirements:

* **Format**: PNG, JPG, WebP
* **Size**: ≤ 300 KB recommended
* **Encoding**: Base64 with MIME prefix or direct URL

**Base64 format:**

```
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...
```

**URL format:**

```
https://yourgame.com/images/gold-coins.png
```

***

## Best Practices

1. **Authentication**: See [Authentication](/authentication) for access token management
2. **Idempotency**: Use [Idempotency-Key](/idempotency) header to prevent duplicate operations
3. **Webhook verification**: Always verify webhook signatures (see [Webhooks](/webhooks))
4. **Error handling**: Implement proper error handling for all payment statuses
5. **User experience**: Show loading states and clear success/failure messages
6. **Security**: Use HTTPS for all webhook endpoints and return URLs
7. **Testing**: Use sandbox mode for development and testing

***

## Error Handling

Common payment errors and how to handle them:

* `invalid_game_id`: Check your game registration and access token (see [Authentication](/authentication))
* `invalid_currency`: Ensure currency code is supported (USD, EUR, etc.)
* `invalid_price`: Price must be a positive integer in cents
* `invalid_image`: Check image format and size requirements
* `session_expired`: Payment session has expired, create a new one
* `insufficient_funds`: User's payment method was declined

***

## Testing

Use the [Sandbox](/sandbox) environment for development and testing. Test cards and OTP codes are available there.
